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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15100824/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 19:07:49  来源:igfitidea点击:

How do I create a global variable that is thread-specific in C using POSIX threads?

clinuxmultithreadingpthreadsposix

提问by Cal

I am learning about POSIX threads and I have come to the section on Thread Specific Data. The book does an excellent example using a file descriptor. However, I wanted to do the same example on my own, except this time using a global variable. However, I am having some difficulty fully grasping this concept.

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

What I want to do is the following:

我想要做的是以下内容:

  • Create a global integer
  • Declare a key for the global int
  • 创建一个全局整数
  • 为全局 int 声明一个键

in main:

在主要:

  • set global integer to be a value eg. 10
  • create a key for it without any clean up
  • create 4 threads and send them to execute thread_func
  • check if value is still 10, since threads only see a copy of it
  • 将全局整数设置为一个值,例如。10
  • 为它创建一个密钥,无需任何清理
  • 创建4个线程并发送它们执行thread_func
  • 检查值是否仍然为 10,因为线程只能看到它的副本

in thread_func:

在线程函数中:

  • use pthread_setspecific(key,global variable) to create a local instance - not sure if I am interpreting this correctly
  • call a function - dosomething()
  • exit
  • 使用 pthread_setspecific(key,global variable) 创建一个本地实例 - 不确定我是否正确解释了这个
  • 调用函数 - dosomething()
  • 出口

in do_something

在做某事

  • create a local pointer and assign it to pthread_getspecific(key) - this should get me a thread specific version of the global variable
  • change the value of what is stored at the local pointer to 2
  • exit
  • 创建一个本地指针并将其分配给 pthread_getspecific(key) - 这应该为我提供全局变量的线程特定版本
  • 将本地指针中存储的值更改为 2
  • 出口

Here's the code:

这是代码:

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

From what I understood, when you call pthread_getspecific, each thread is supposed to have its own unique memory address for the memory addresses - which I did not find to be the case here. I know I am not going about this correctly and when I did try to look at the memory addresses for each thread when doing getspecific, I saw the same memory address. Perhaps someone can point me to an example where they use a global variable (not file descriptors) and they have thread-specific usage in which threads look at it as a local variable.

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

采纳答案by WhozCraig

The purpose of TLS (thread-local storage) is to provide an defined mechanism whereby code can retrieve thread-specific data stored in a database accessed by a all-threads-known shared key. Your code is storing the samedata in TLS: the address of a single global variable). Therefore when a thread requests this data using the tls-key, they will all get back the sameaddress.

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

I think you intend your code to do something like this:

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

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

Output

输出

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

回答by paulsm4

In general, what you're looking for is "thread local storage". Here are a few links:

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

Choosing pthread_getspecific()is correct. Here's a good example:

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

Please review the above example: I think it will point you to the problem ... or suggest a good alternative.

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

IMHO...

恕我直言...

回答by Nominal Animal

This is not an answer, but a side note:

这不是答案,而是旁注:

If you are working on Linux-specific code, you can use the __threadkeyword. Essentially,

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

static __thread int counter = 5;

creates a different countervariable for each thread, initialized to value 5 whenever a new thread gets created. Such code is future-compatible with C11, since C11 standardized the same semantics using the _Thread_localkeyword. This is much saner than the POSIX thread-specific functions (which have implementation-specific limits, and are quite cumbersome compared to the __threadkeyword), on all architectures using C, except those that have declared C99 and later "standard non grata" (i.e., Microsoft).

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

See the Thread-Local Storage chapterin GCC documentationfor details.

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