如何正确使用线程特定的数据

Posted

技术标签:

【中文标题】如何正确使用线程特定的数据【英文标题】:How to use Thread-specific data correctly 【发布时间】:2013-01-10 14:54:36 【问题描述】:

我正在使用 pthread 进行编程。我需要一个全局变量,它对不同的线程具有不同的值。并且线程将使用相同的函数来处理这个变量,例如改变它的值。如果一个线程改变了它的值,其他线程中的值不会改变。所以我尝试使用线程特定的数据,并写了一个例子。我需要将 pthread 操作包装在函数中。例如:setspecific()、changedata、printdata()、create_key()、delete_key()等

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

pthread_key_t key;
pthread_key_t key2;

struct test_struct 
    int i;
    float k;
struct_data;

int temp;

int setspecificvar ()  /* Set specific data for threads */

    pthread_setspecific (key, &struct_data);
    pthread_setspecific (key2, &temp);

    return 0;

int changedata (int i, float k, int tempvar)  /* Change specific data for threads */

    temp = tempvar;
    struct_data.i = i;
    struct_data.k = k;

    return 0;


int printdata (int t)   /* print specific data for threads */

    printf ("The addres in child%d returned from pthread_getspecific(key):0x%p\n",                           \
            t, (struct test_struct *)pthread_getspecific(key));

    printf ("The value of members in structure bound to \"key\" in  child%d:\nstruct_data.i:%d\nstruct_data.k: %f\n", \
            t, ((struct test_struct *)pthread_getspecific (key))->i,                            \
            ((struct test_struct *)pthread_getspecific(key))->k);

    printf ("------------------------------------------------------\n");

    printf ("The addres in child%d returned from pthread_getspecific(key2):0x%p\n",                          \
            t, (int *)pthread_getspecific(key2));
    printf ("The value of \"temp\" bound to \"key2\" in child%d:%d\n", \
            t, *((int *)pthread_getspecific(key2)));

    return 0;


void *child1 (void *arg)

    setspecificvar ();
    changedata(10, 3.141500, 110); /* Should not change the data in child2 */
    printdata(1);


void *child2 (void *arg)

    /* sleep (2); */
    setspecificvar ();

    changedata(12, 2.141500, 120); /* Should not change the data in child1 */
    printdata(2);

    changedata (122, 22.141500, 1220); /* Should not change the data in child1 */
    printdata (2);


int create_key () 
    pthread_key_create (&key, NULL);
    pthread_key_create (&key2, NULL);
    return 0;


int delete_key () 

    pthread_key_delete (key);
    pthread_key_delete (key2);
    return 0;


int main (void)

    pthread_t tid1, tid2;

    create_key ();
    pthread_create (&tid1, NULL, (void *)child1, NULL);
    pthread_create (&tid2, NULL, (void *)child2, NULL);
    pthread_join (tid1, NULL);
    pthread_join (tid2, NULL);

    delete_key ();

    return 0;

我创建了两个线程。当我让一个线程休眠 2 秒时。我得到了正确的答案。

The addres in child1 returned from pthread_getspecific(key):0x0x8049c98
The value of members in structure bound to *"key" in  child1*:
*struct_data.i:10
struct_data.k: 3.141500*
------------------------------------------------------
The addres in child1 returned from pthread_getspecific(key2):0x0x8049ca0
The value of "temp" bound to *"key2" in child1*:110
The addres in child2 returned from pthread_getspecific(key):0x0x8049c98
The value of members in structure bound to "key" in  child2:
struct_data.i:12
struct_data.k: 2.141500
------------------------------------------------------
The addres in child2 returned from pthread_getspecific(key2):0x0x8049ca0
The value of "temp" bound to "key2" in child2:120
The addres in child2 returned from pthread_getspecific(key):0x0x8049c98
The value of members in structure bound to "key" in  child2:
struct_data.i:122
struct_data.k: 22.141500
------------------------------------------------------
The addres in child2 returned from pthread_getspecific(key2):0x0x8049ca0
The value of "temp" bound to "key2" in child2:1220

当我评论 /* sleep(2); */ ,我得到不正确的答案。

The addres in child1 returned from pthread_getspecific(key):0x0x8049c54
The addres in child2 returned from pthread_getspecific(key):0x0x8049c54
The value of members in structure bound to "key" in  child2:
*struct_data.i:12
struct_data.k: 2.141500*
The value of members in structure bound to *"key" in  child1*:
struct_data.i:12
struct_data.k: 2.141500
------------------------------------------------------
The addres in child1 returned from pthread_getspecific(key2):0x0x8049c5c
The value of "temp" bound to *"key2" in child1*:120
------------------------------------------------------
The addres in child2 returned from pthread_getspecific(key2):0x0x8049c5c
The value of "temp" bound to "key2" in child2:120
The addres in child2 returned from pthread_getspecific(key):0x0x8049c54
The value of members in structure bound to "key" in  child2:
struct_data.i:122
struct_data.k: 22.141500
------------------------------------------------------
The addres in child2 returned from pthread_getspecific(key2):0x0x8049c5c
The value of "temp" bound to "key2" in child2:1220

我想在不休眠线程的情况下获得正确的结果。一个线程不应该等待另一个线程完成调用 pthread_setspecific() ,对吗?我该怎么办?感谢您的考虑。我将 struct_data 定义为全局变量是否正确?谁能帮帮我?

【问题讨论】:

至少对于gcc为什么不使用存储类关键字__thread来声明一个变量为线程本地存储? 【参考方案1】:
int setspecificvar ()  /* Set specific data for threads */

    pthread_setspecific (key, &struct_data);
    pthread_setspecific (key2, &temp);

    return 0;

在这里,您在每个线程中明确地将keykey2 设置为相同的值,因此它在每个线程中具有相同的值也就不足为奇了。尝试在每个线程中将其设置为 不同 值,然后它将在每个线程中具有不同的值。

一个常见的模式是这样的:

    致电pthread_getspecific。如果它返回非 NULL,则使用该指针。

    如果返回 NULL,则使用动态分配线程特定对象的新实例。调用 pthread_setspecific 以确保从此线程对 pthread_getspecific 的下一次调用返回相同的对象。

    pthread_key_create 调用中,一定要注册一个析构函数,当线程消失时释放线程特定对象的实例。

这将为每个线程提供自己的结构实例。

例如:

int setspecificvar ()  /* Set specific data for threads */

    struct test_struct *s = malloc(sizeof(struct test_struct));
    int *i = malloc(sizeof(int *));
    memset(s, 0, sizeof(s));
    memset(i, 0, sizeof(i));

    pthread_setspecific (key, s);
    pthread_setspecific (key2, i);

    return 0;

这实际上在每个线程中设置了不同的值。还有这个:

int changedata (int i, float k, int tempvar)  /* Change specific data for threads */

    struct test_struct *struct_data = pthread_getspecific(key);
    int *temp = pthread_getspecific(key2);

    *temp = tempvar;
    struct_data->i = i;
    struct_data->k = k;

    return 0;

这实际上使用线程特定的数据。

【讨论】:

我已经调用 changedata() 来更改每个线程中 struct_data 和 temp 的值。 @大卫施瓦茨 你是说将不同的变量绑定到不同线程中的键? 你能举个例子吗?我真的很困惑这个问题。谢谢你的时间。 @大卫施瓦茨 @NickDong: changedata 改变了tempstruct_data 的内容,但它们是所有线程共享的全局变量,而不是线程特定的数据。您拥有的唯一特定于线程的数据存储这些全局结构的地址,您在每个线程中将其设置为相同的值。在我的回答中使用该模式。 现在,我明白你说什么了。购买为什么在调用 malloc() 之后调用 memset()。 Malloc() 已经为变量分配了内存,对吧?我重新编写了代码,但仍然遇到 Segmentation fault (core dumped) 。我在http://codeviewer.org/view/code:2e3b 提交我的代码。你能再帮帮我吗? @大卫施瓦茨

以上是关于如何正确使用线程特定的数据的主要内容,如果未能解决你的问题,请参考以下文章

如何让线程按特定顺序执行?

如何正确管理 MySQL 连接?

如何正确使用 Core Data 进行多线程处理? [关闭]

如何从 API 获取和显示特定 JSON 数据的正确方法

如何显示屏幕并运行一些“后台”任务(不使用线程)

如何正确退出 Pyside2 多线程 GUI 应用程序?