pthread线程特定数据

线程特定数据,也被称为线程私有数据,是一种存储和查找一个特定线程相关数据的机制。我们称这个数据为线程特定或线程私有的原因,是因为每个线程访问它自己独立的数据拷贝,而不用担心和其它线程的访问的同步。

线程特定数据看似很复杂,其实我们可以把它理解为就是一个索引指针。key结构中存储的是索引,pthread结构中存储的是指针,指向线程中的私有数据,通常是malloc函数返回的指针。

POSIX要求实现POSIX的系统为每个进程维护一个称之为Key的结构数组(如图1所示),这个数组中的每个结构称之为一个线程特定数据元素。POSIX规定系统实现的Key结构数组必须包含不少于128个线程特定元素,而每个线程特定数据元素至少包含两项内容:使用标志和析构函数指针。key结构中的标志指示这个数组元素是否使用,所有的标志初始化为“不在使用”。

当一个线程调用pthread_key_create创建一个新的线程特定数据元素时,系统搜索其所在进程的Key结构数组,找出其中第一个未使用的元素,并通过keyptr返回该元素的键,即就是我们前面说的索引。pthread_key_create函数的第二个参数destructor是一个函数指针,指向一个析构函数,用于线程结束以后的一些后期后期处理工作,析构函数的额参数就是线程特定数据的指针。

除了进程范围内地的key结构数组外,系统还在进程中维护关于每个线程的线程结构,把这个特定于线程的结构称为pthread结构,它的部分内容是于key数组对应的指针数组(如图2所示),它的128个指针和进程中的128个可能的键(索引)是逐一关联的。指针指向的内存就是线程特有数据。

下面看一个具体的过程,启动一个进程并创建了若干线程,其中一个线程(比如线程1),要申请线程私有数据,系统调用pthread_key_creat()在图1所示的key结构数组中找到第一个未用的元素,并把它的键,也就是看面说的索引(0-127),返回给调用者,假设返回的索引是1,线程之后通过pthrea_getspecific()调用获得本线程的pkey[1]值,返回的是一个空指针ptr = null,这个指针就是我们可以通过索引1使用的线程数据的首地址了,但是他现在为空,因此根据实际情况用malloc分配一快内存,在使用pthread_setspecific()调用将特定数据的指针指向刚才分配到内存区域。整个过程结束后key结构和pthread结构如图3所示,

在操作线程特定数据时涉及到的函数调用有一下几个:

#include <pthread.h>

//成功返回0,失败返回错误号。

int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *)); 

//返回线程特定数据,或者如果没有值关联到这个关键字时返回NULL。 

void *pthread_getspecific(pthread_key_t key); 

//成功返回0,失败返回错误号。

int pthread_setspecific(pthread_key_t key, const void *value); 

pthread_once_t initflag = PTHREAD_ONCE_INIT;

//成功返回0,失败返回错误码。

int pthread_once(pthread_once_t *initflag, void (*initfn)(void)); 

//成功返回0,失败返回错误号。

int pthread_key_delete(pthread_key_t *key);

线程特定数据的典型用法如下:

void destructor(void *) 

pthread_key_t key; 

pthread_once_t init_done = PTHREAD_ONCE_INIT; 

void 

thread_init(void) 

{ 

    err = pthread_key_create(&key, destructor); 

} 

int threadfunc(void *arg) 

{ 

    pthread_once(&init_done, thread_init); 

    if( (ptr = pthread_getspecific(key)) == NULL ){
        ptr = malloc(len);
        pthread_setspecific(key,ptr);
        ...
    }
    ... 

}

当调用pthread_key_create 后会产生一个所有线程都可见的线程特定数据(TSD)的键值(如上图中所有的线程都会得到一个pkey[1]的值), 但是这个键所指向的真实数据却是不同的,虽然都是pkey[1], 但是他们并不是指向同一块内存,而是指向了只属于自己的实际数据, 因此, 如果线程0更改了pkey[1]所指向的数据, 而并不能够影像到线程n;

在线程调用pthread_setspecific后会将每个线程的特定数据与thread_key_t绑定起来,虽然只有一个pthread_key_t,但每个线程的特定数据是独立的内存空间,当线程退出时会执行destructor 函数。

/** 示例1: 设置/获取线程特定数据
在两个线程中分别设置/获取线程特定数据, 查看两个线程中的数据是否是一样的(肯定是不一样的O(∩_∩)O~)
**/
pthread_key_t key;
typedef struct Tsd
{
    pthread_t tid;
    char *str;
} tsd_t;
//用来销毁每个线程所指向的实际数据
void destructor_function(void *value)
{
    free(value);
    cout << "destructor ..." << endl;
}  

void *thread_routine(void *args)
{
    //设置线程特定数据
    tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
    value->tid = pthread_self();
    value->str = (char *)args;
    pthread_setspecific(key, value);
    printf("%s setspecific, address: %p\n", (char *)args, value);  

    //获取线程特定数据
    value = (tsd_t *)pthread_getspecific(key);
    printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
    sleep(2);  

    //再次获取线程特定数据
    value = (tsd_t *)pthread_getspecific(key);
    printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);  

    pthread_exit(NULL);
}  

int main()
{
    //这样每个线程当中都会有一个key可用了,
    //但是每个key所绑定的实际区域需要每个线程自己指定
    pthread_key_create(&key, destructor_function);  

    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");
    pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");  

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_key_delete(key);  

    return 0;
}  
/** 示例2:运用pthread_once, 让key只初始化一次
注意: 将对key的初始化放入到init_routine中
**/
pthread_key_t key;
pthread_once_t once_control = PTHREAD_ONCE_INIT;
typedef struct Tsd
{
    pthread_t tid;
    char *str;
} tsd_t;  

//线程特定数据销毁函数,
//用来销毁每个线程所指向的实际数据
void destructor_function(void *value)
{
    free(value);
    cout << "destructor ..." << endl;
}  

//初始化函数, 将对key的初始化放入该函数中,
//可以保证inti_routine函数只运行一次
void init_routine()
{
    pthread_key_create(&key, destructor_function);
    cout << "init..." << endl;
}  

void *thread_routine(void *args)
{
    pthread_once(&once_control, init_routine);  

    //设置线程特定数据
    tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
    value->tid = pthread_self();
    value->str = (char *)args;
    pthread_setspecific(key, value);
    printf("%s setspecific, address: %p\n", (char *)args, value);  

    //获取线程特定数据
    value = (tsd_t *)pthread_getspecific(key);
    printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
    sleep(2);  

    //再次获取线程特定数据
    value = (tsd_t *)pthread_getspecific(key);
    printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);  

    pthread_exit(NULL);
}  

int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");
    pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");  

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_key_delete(key);  

    return 0;
}  

https://blog.csdn.net/zjf280441589/article/details/43883033

http://www.360doc.com/content/18/0617/11/56818466_763036051.shtml



原文地址:https://www.cnblogs.com/tianzeng/p/9192340.html

时间: 2024-10-22 07:38:52

pthread线程特定数据的相关文章

Linux多线程实践(4) --线程特定数据

线程特定数据 int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)); int pthread_key_delete(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *pointer); void * pthread_getspecific(pthread_key_t key); pthread_onc

线程特定数据TSD总结

一线程的本质 二线程模型的引入 三线程特定数据 四关键函数说明 五刨根问底啥原理 六私有数据使用示例 七参考文档 一.线程的本质 Linux线程又称轻量进程(LWP),也就说线程本质是用进程之间共享用户空间模拟实现的. 二.线程模型的引入 线程模型引入是为了数据共享,为什么又引入线程私有数据?有时候想让基于进程的接口适应多线程环境,这时候就需要为每个线程维护一份私有数据了,最典型的就是errno了. 在维护每个线程的私有数据的时候,我们可能会想到分配一个保存线程数据的数组,用线程的ID作为数组的

【C/C++多线程编程之十】pthread线程私有数据

多线程编程之线程私有数据 Pthread是 POSIX threads 的简称,是POSIX的线程标准.  线程同步从互斥量[C/C++多线程编程之六]pthread互斥量,信号量[C/C++多线程编程之七]pthread信号量,条件变量[C/C++多线程编程之八]pthread条件变量,读写锁[C/C++多线程编程之九]pthread读写锁,多线程的同步机制已经有了清晰深入的探究,多线程编程的精髓所在,需要深入理解.        线程私有数据TSD(Thread-specific Data)

线程特定数据

1 #include <pthread.h> 2 #include <stdio.h> 3 #include <string.h> 4 #include <stdlib.h> 5 6 7 pthread_key_t thread_self_data_key; 8 9 void destory(void *p) 10 { 11 free(p); 12 } 13 14 void createit(void) 15 { 16 pthread_key_create(

为线程特定数据创建键

#include <stdlib.h> #include <pthread.h> #include <stdio.h> #include <sched.h> #include<string.h> pthread_key_t key; void destructor(void *data) //如果创建该键时指定了destructor 函数,则该线程终止时,系统会调用destructor 函数,传进的参数是绑定的值. { if(data != NU

Linux多线程实践(四 )线程的特定数据

在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据, 然而在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有.但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问.POSIX线程库通过维护一定的数据结构来解决这个问题,这个些数据称为(Thread-specific-data或 TSD). 相关函数如下: int pthread_key_create(pthread_key_t *key, void (*destr

【C/C++多线程编程之四】终止pthread线程

多线程编程之终止pthread线程  Pthread是 POSIX threads 的简称,是POSIX的线程标准.           终止线程似乎是多线程编程的最后一步,但绝不是本系列教材的结束.线程创建到线程终止,希望先给读者一个关于多线程编程的总体认识.           1.终止Pthread线程:pthread_exit()           參数:           ret:地址指针,本质上是返回值写入的地址.           终止线程是线程的主动行为,一个线程调用pth

12.6 线程私有数据

线程私有数据是一种用于存储和获取与特定线程相关联数据的机制,称为线程特定的或者是线程私有的,是因为我们希望每个线程都可以独立访问其独有的数据,而不用担心与其他线程的同步访问问题. 许多人费力实现了促进进程数据以及属性贡献的线程模型,那么为什么还有人想要实现一个接口,在这样一个模型中防止共享呢?有如下两点原因: 首先,有些时候我们需要以线程为基础维护一些数据,因为没有任何机制可以保证线程ID总是比较小的,且是连续的整数,因此我们不能简单地将每一个线程的私有数据分配为一个数组,然后使用线程ID作为索

【C/C++多线程编程之五】pthread线程深入理解

多线程编程之pthread线程深入理解  Pthread是 POSIX threads 的简称,是POSIX的线程标准. 前几篇博客已经能给你初步的多线程概念.在进一步学习线程同步等多线程核心知识之前,须要对多线程深入的理解.非常多人忽略或者回避这部分内容,直接的问题是学习者无法把握多线程编程的内在原理,理解的层次太浅.           1.进程资源:                      进程资源有存储资源与其它资源.其它资源包括环境变量.地址,文件等.存储资源.进程的内存分配,博客[