如何使用POSIX线程在C中创建特定于线程的全局变量?

Cal*_*Cal 11 c linux multithreading posix pthreads

我正在学习POSIX线程,我已经开始讨论线程特定数据.本书使用文件描述符做了一个很好的例子.但是,我想自己做同样的例子,除了这次使用全局变量.但是,我在完全理解这个概念时遇到了一些困难.

我想做的是以下内容:

  • 创建一个全局整数
  • 声明全局int的键

在主要:

  • 将全局整数设置为例如值.10
  • 为它创建一个密钥而不需要任何清理
  • 创建4个线程并发送它们以执行thread_func
  • 检查值是否仍为10,因为线程只看到它的副本

在thread_func中:

  • 使用pthread_setspecific(键,全局变量)来创建一个本地实例 - 不确定我是否正确解释了这一点
  • 调用函数 - dosomething()
  • 出口

在do_something

  • 创建一个本地指针并将其分配给pthread_getspecific(key) - 这应该让我得到全局变量的线程特定版本
  • 将本地指针中存储的值的值更改为2
  • 出口

这是代码:

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

#define NUMTHREADS 4

pthread_key_t glob_var_key;
int glob_var;

void do_something()
{
    //get thread specific data
    int* glob_spec_var = (int*) pthread_getspecific(glob_var_key);
    printf("Thread %d glob_spec before mod value is %d\n", (unsigned int) pthread_self(), *glob_spec_var);
    *glob_spec_var = 2;
    printf("Thread %d glob_spec after mod value is %d\n", (unsigned int) pthread_self(), *glob_spec_var);
}

void* thread_func(void *arg)
{
    pthread_setspecific(glob_var_key, &glob_var);
    do_something();
    pthread_exit(NULL);
}

int main(void)
{
    pthread_t threads[NUMTHREADS];
    int i;
    glob_var = 10;
    pthread_key_create(&glob_var_key,NULL);
    printf("Main: glob_var is %d\n", glob_var);
    for (i=0; i < NUMTHREADS; i++)
    {
        pthread_create(&threads[i],NULL,thread_func,NULL);
    }

    for (i=0; i < NUMTHREADS; i++)
    {
        pthread_join(threads[i], NULL);
    }
    printf("Main: glob_var is %d\n", glob_var);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,当你调用pthread_getspecific时,每个线程应该有自己唯一的内存地址的内存地址 - 我在这里没有发现这种情况.我知道我没有正确地解决这个问题,当我在做特定操作时尝试查看每个线程的内存地址时,我看到了相同的内存地址.也许有人可以指出我使用全局变量(不是文件描述符)的示例,并且它们具有特定于线程的用法,其中线程将其视为局部变量.

Nom*_*mal 18

这不是答案,而是旁注:

如果您正在使用特定于Linux的代码,则可以使用该__thread关键字.实质上,

static __thread int counter = 5;
Run Code Online (Sandbox Code Playgroud)

counter为每个线程创建一个不同的变量,每当创建一个新线程时初始化为值5.这样的代码与C11是未来兼容的,因为C11使用_Thread_local关键字标准化了相同的语义.这比__thread使用C的所有架构上的POSIX线程专用函数(具有特定于实现的限制,并且与关键字相比非常麻烦)更加安全,除了那些已声明C99以及之后" 标准不受欢迎 "的架构(即,微软).

参见线程局部存储的章节GCC文档的详细信息.


Who*_*aig 15

TLS(线程局部存储)的目的是提供一种定义的机制,由此代码可以检索存储在由全线程已知共享密钥访问的数据库中的特定于线程的数据.您的代码在TLS中存储相同的数据:单个全局变量的地址).因此,当线程使用tls-key请求此数据时,它们将返回相同的地址.

我想你打算用你的代码做这样的事情:

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

#define NUMTHREADS 4

pthread_key_t glob_var_key;

void do_something()
{
    //get thread specific data
    int* glob_spec_var = pthread_getspecific(glob_var_key);
    printf("Thread %d before mod value is %d\n", (unsigned int) pthread_self(), *glob_spec_var);
    *glob_spec_var += 1;
    printf("Thread %d after mod value is %d\n", (unsigned int) pthread_self(), *glob_spec_var);
}

void* thread_func(void *arg)
{
    int *p = malloc(sizeof(int));
    *p = 1;
    pthread_setspecific(glob_var_key, p);
    do_something();
    do_something();
    pthread_setspecific(glob_var_key, NULL);
    free(p);
    pthread_exit(NULL);
}

int main(void)
{
    pthread_t threads[NUMTHREADS];
    int i;

    pthread_key_create(&glob_var_key,NULL);
    for (i=0; i < NUMTHREADS; i++)
        pthread_create(threads+i,NULL,thread_func,NULL);

    for (i=0; i < NUMTHREADS; i++)
        pthread_join(threads[i], NULL);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

产量

Thread 2625536 before mod value is 1
Thread 741376 before mod value is 1
Thread 3162112 before mod value is 1
Thread 3698688 before mod value is 1
Thread 2625536 after mod value is 2
Thread 741376 after mod value is 2
Thread 3162112 after mod value is 2
Thread 3698688 after mod value is 2
Thread 2625536 before mod value is 2
Thread 741376 before mod value is 2
Thread 3162112 before mod value is 2
Thread 3698688 before mod value is 2
Thread 2625536 after mod value is 3
Thread 741376 after mod value is 3
Thread 3162112 after mod value is 3
Thread 3698688 after mod value is 3
Run Code Online (Sandbox Code Playgroud)