16

我正在学习 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 时尝试查看每个线程的内存地址时,我看到了相同的内存地址。也许有人可以给我举个例子,他们使用全局变量(而不是文件描述符)并且他们具有线程特定的用法,其中线程将其视为局部变量。

4

3 回答 3

20

这不是答案,而是附注:

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

static __thread int counter = 5;

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

有关详细信息,请参阅GCC 文档中的线程本地存储章节

于 2013-02-27T12:05:00.863 回答
19

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

输出

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
于 2013-02-26T23:07:12.837 回答
4

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

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

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

恕我直言...

于 2013-02-26T22:50:09.687 回答