【问题标题】:How do I create a global variable that is thread-specific in C using POSIX threads?如何使用 POSIX 线程在 C 中创建特定于线程的全局变量?
【发布时间】:2013-02-12 14:16:50
【问题描述】:

我正在学习 POSIX 线程,我已经来到线程特定数据部分。这本书使用文件描述符做了一个很好的例子。但是,我想自己做同样的例子,除了这次使用全局变量。但是,我很难完全掌握这个概念。

我想做的是:

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

主要:

  • 将全局整数设置为一个值,例如。 10
  • 无需任何清理即可为其创建密钥
  • 创建4个线程并发送给执行thread_func
  • 检查值是否仍为 10,因为线程只看到它的副本

在线程函数中:

  • 使用 pthread_setspecific(key,global variable) 创建一个本地实例 - 不确定我是否正确解释了这个
  • 调用函数 - dosomething()
  • 退出

在做某事

  • 创建一个本地指针并将其分配给 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;
}

据我了解,当您调用 pthread_getspecific 时,每个线程都应该有自己唯一的内存地址作为内存地址——我在这里没有发现这种情况。我知道我没有正确处理这个问题,当我在执行 getspecific 时尝试查看每个线程的内存地址时,我看到了相同的内存地址。也许有人可以给我举个例子,他们使用全局变量(而不是文件描述符)并且他们具有线程特定的用法,其中线程将其视为局部变量。

【问题讨论】:

  • 您不应该在每个线程键中设置 same 地址。每个线程在线程启动期间将它们的唯一数据设置为公共键。有关其他示例,请参阅this example。当do_something() 被触发时,它将在指定键处获取当前线程特定的数据。这允许通用代码对当前线程的数据进行操作,而不是自己在一些线程到数据的映射中维护它。
  • 感谢您的示例。我只是想找到一个使用全局变量但线程可以在本地修改它的体面示例,并且这些更改只能由该线程看到。这只是为了练习。请您详细说明地址部分,如果不是太麻烦,请告诉我正确的方法?
  • 您是否希望在正确设置后,每个线程都将其 自己的 变量存储在 thread-local-storage (TLS) 中?这个问题的答案对于了解准确你的最终目标是至关重要的。如果您希望所有线程共享 same 全局,则需要使用互斥锁或临界区。那么可以详细说明一下吗=P?
  • 哦,我太傻了。我明白我做错了什么——这个例子非常好。我完全误解了这一点。他们 malloc 为每个线程函数的结构,然后 setspecific - 然后调用下一个函数,以便该函数将看到与该线程有关的数据,并且看起来像每个线程的本地副本。我的推理是否正确?
  • "您是否希望在正确设置后,每个线程都有自己的变量存储在线程本地存储 (TLS) 中?答案对于准确了解您的最终目标至关重要是。如果你希望所有线程共享同一个全局,那么互斥锁或临界区是有序的。所以你能详细说明一下 =P 吗? - WhozCraig 3 分钟前“我最初的想法是我创建了一个全局变量,并且 getspecific 和 setspecific 会为每个线程创建该变量的本地副本 - 我不认为会发生这种情况。

标签: c linux multithreading pthreads posix


【解决方案1】:

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

我认为你打算让你的代码做这样的事情:

#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;
}

输出

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

【讨论】:

  • 这正是我要找的
【解决方案2】:

这不是答案,而是附注:

如果您正在处理特定于 Linux 的代码,则可以使用 __thread 关键字。本质上,

static __thread int counter = 5;

为每个线程创建一个不同的counter 变量,每当创建新线程时初始化为值 5。这样的代码与 C11 未来兼容,因为 C11 使用 _Thread_local 关键字标准化了相同的语义。这比使用 C 的所有架构上的 POSIX 线程特定函数(具有特定于实现的限制,并且与 __thread 关键字相比相当麻烦)要理智得多,除了那些声明了 C99 和更高版本的架构“标准不受欢迎的”(即微软)。

详情请参阅GCC documentation 中的Thread-Local Storage chapter

【讨论】:

    【解决方案3】:

    一般来说,您正在寻找的是“线程本地存储”。这里有几个链接:

    选择pthread_getspecific() 是正确的。这是一个很好的例子:

    请查看上面的示例:我认为它会为您指出问题所在......或提出一个好的替代方案。

    恕我直言...

    【讨论】:

      猜你喜欢
      • 2020-03-15
      • 2013-11-16
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 2015-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多