从另一个线程访问主线程的局部变量

时间:2020-11-03 19:57:44

标签: c multithreading pthreads posix

以下是Computer Systems: A Programmer's Perspective书中的示例程序,作者在其中说明了多线程程序中共享变量的用法:

#include "csapp.h"

#define N 2

void *thread(void *vargp);

char **ptr; /* Global variable */

int main()
{
    int i;
    pthread_t tid;
    char *msgs[N] = {
        "Hello from foo",
        "Hello from bar"
    };

    ptr = msgs;
    for (i = 0; i < N; i++)
        Pthread_create(&tid, NULL, thread, (void *)i);
    Pthread_exit(NULL);
}

void *thread(void *vargp)
{
    int myid = (int)vargp;
    static int cnt = 0;
    printf("[%d]: %s (cnt=%d)\n", myid, ptr[myid], ++cnt);
    return NULL;
}

可以看出,两个线程都访问全局变量ptr,该全局变量指向调用msgs的主线程的局部变量pthread_exit

现在,根据pthread_exit的文档:

线程终止后,不确定访问该线程的本地(自动)变量的结果。

那么,上面的代码正确吗?

即使主线程调用pthread_exit,从另一个线程访问主线程的局部变量是否合法?

2 个答案:

答案 0 :(得分:2)

<块引用>

即使主线程调用pthread_exit,从另一个线程访问主线程的局部变量是否合法?

没有。一旦主线程通过 pthread_exit 完成,任何局部变量的生命周期(即自动存储持续时间)就结束了。来自另一个线程的任何进一步访问都是未定义行为

<块引用>

那么,上面的代码正确吗?

没有。 msgs 数组是一个局部变量,其生命周期在 pthread_exit 调用后结束。因此,未定义通过 ptr 进行的任何进一步访问。

一般来说,只要这些对象的生命周期没有结束,从另一个线程访问一个线程的局部变量是有效的。这由 POSIX 保证:

<块引用>

[..] 任何地址可以由线程确定的东西,包括 但不限于静态变量,通过 malloc() 获得的存储, 通过实现定义获得的可直接寻址的存储 函数和自动变量可供所有线程访问 同样的过程。

另一件要注意的事情是,如果您已将 msgs[0]msgs[1] 传递给线程,那将是有效的。因为 msgs[0]msgs[1] 指向的字符串文字的生命周期仅在程序终止时结束(不仅仅是具有指向它的指针的线程)。

同样,如果您通过 malloc(或其朋友)或任何具有静态存储持续时间的对象分配并将其传递给线程函数,则它是有效的。

例如

#include<stdio.h>
#include<pthread.h>

void *thread_func(void *arg)
{
    char *str = p;
    printf("%s\n", str);
    return NULL;
}

int main(void)
{
    pthread_t tid;
    char *p = "Hello";
    pthread_create(&tid, NULL, thread_func, p);
    pthread_exit(NULL);
}

这完全没问题,因为 p 指向一个即使在 main 通过 pthread_exit 返回后仍然存在的字符串文字。

所以这里要注意的重要区别是,对另一个线程中的变量的访问是否未定义取决于所述对象的生命周期,而不仅仅是变量碰巧被定义的位置。< /p>

答案 1 :(得分:-1)

以下建议的代码:

  1. 干净地编译
  2. 执行所需的功能
  3. 证明每个线程都在自己的时间内运行,而不是(有必要)按照其创建顺序运行。
  4. 消除“未知”头文件:csapp.h
  5. 包括应用程序所需的头文件
  6. 显示main()的“有效”签名
  7. 演示如何正确使用tid变量/数组
  8. 演示如何退出线程函数
  9. 演示如何让main()等待线程功能完成
  10. 使用size_t,因此没有隐式转换
  11. 演示如何打印size_t变量
  12. 展示了pthread_*函数的一些错误处理

现在,建议的代码:

//#include "csapp.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define N 2

void *thread(void *vargp);

char **ptr; /* Global variable */

int main( void )
{
    pthread_t tid[N];
    char *msgs[N] = {
        "Hello from foo",
        "Hello from bar"
    };

    ptr = msgs;
    
    for ( size_t i = 0; i < N; i++)
    {
        if( pthread_create(&tid[i], NULL, thread, (void *)i) != 0)
        {
            fprintf( stderr, "pthread_create failed\n" );
            exit( EXIT_FAILURE );
        }
    }
    
    for( size_t i= 0; i<N; i++ )
    {
        if( pthread_join( tid[i], NULL ) != 0 )
        {
            fprintf( stderr, "pthread_join failed\n" );
            exit( EXIT_FAILURE );
        }
    }
}

void *thread(void *vargp)
{
    size_t myid = ( size_t )vargp;
    static int cnt = 0;
    printf("[%zu]: %s (cnt=%d)\n", myid, ptr[myid], ++cnt);
    pthread_exit( NULL );
}

典型的代码运行结果为:

[0]: Hello from foo (cnt=1)
[1]: Hello from bar (cnt=2)

注意:输出也可以是:

[1]: Hello from bar (cnt=1)
[0]: Hello from foo (cnt=2)