概述:
线程特定数据,也称为线程私有数据,是存储和查询某个特定数据相关数据的一种机制。
在单线程程序中,我们经常要用到“全局变量”以实现多个函数间共享数据。
在多线程环境下,由于数据空间是共享的,因此全局变量也为所有所有线程所共有。
但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。
POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据被称为线程特定数据(Thread-specific Data,或TSD)。
相关函数:
在分配线程特定数据之前,需要创建与该数据关联的键。这个键将用于获取对线程特定数据的访问。使用pthread_key_create函数创建一个键。
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void*));
创建的键存储在pkey指向的内存单元中,这个键可以被进程中的所有线程使用,但每个线程与不同的线程特定数据地址相关联。创建新键时,每个线程的数据地址设为空值。
除了创建键以外,pthread_key_create可以为该键关联一个可选择的析构函数。当这个线程退出时,如果数据地址已经被置为非空值,那么析构函数就会被调用,它唯一的参数就是该数据地址。如果传入的析构函数为空,就表明没有析构函数与这个键关联。
线程通常使用malloc为线程特定数据分配内存。析构函数通常释放已分配的内存。
对所有的线程,我们都可以通过调用pthread_key_delete函数来取消键与线程特定数据值之间的联系。
int pthread_key_delete(pthread_key_t key);
有些线程可能看到一个键值,而其他的线程看到的可能是另一个不同的键值,这取决于系统是如何调度线程的,解决这种竞争的办法是使用pthread_once函数 。
pthread_once_t once_control = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
once_control必须是一个非本地变量(如全局变量或静态变量),而且必须初始化为PTHREAD_ONCE_INIT。如果每个线程都调用pthread_once,系统就能保证初始化once_control只被调用一次,即系统首次调用pthread_once时。
键一旦创建以后,就可以通过调用pthread_setspecific函数把键和线程特定数据关联起来。可以通过pthread_getspecific获得线程特定数据的地址。
int pthread_setspecific(pthread_key_t key, const void *pointer); void * pthread_getspecific(pthread_key_t key);
如果没有线程特定数据值与键关联,pthread_getspecific将返回一个空指针,我们可以用这个空指针确定是否需要调用pthread_setspecific。
注:参考UNIX高级环境编程(第三版)
测试代码:
#include <unistd.h> #include <sys/types.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ }while(0); \ //自定义线程数据 typedef struct tid { pthread_t tid; char *str; }tsd_t; pthread_key_t key_tsd; pthread_once_t once_control = PTHREAD_ONCE_INIT; void destory_routine(void *value) { printf("destory...\n"); free(value); } void once_routine(void) { pthread_key_create(&key_tsd, destory_routine); printf("key init...\n"); } void *thread_routine(void *arg) { pthread_once(&once_control,once_routine) ; tsd_t *value = (tsd_t*)malloc(sizeof(tsd_t)); value->tid = pthread_self(); value->str = (char*)arg; //设置线程特定数据 pthread_setspecific(key_tsd, value); printf("%s setspecific %p\n",(char*)arg, value); value = (tsd_t*)pthread_getspecific(key_tsd); printf("tid=0x%x str=%s\n",(int)value->tid, value->str); sleep(2); value = (tsd_t*)pthread_getspecific(key_tsd); printf("tid=0x%x str=%s\n",(int)value->tid, value->str); sleep(2); return NULL; } int main(void) { //pthread_key_create(&key_tsd, destory_routine); pthread_t tid1; pthread_t tid2; pthread_create(&tid1, NULL, thread_routine, "thread1"); pthread_create(&tid2, NULL, thread_routine, "thread2"); pthread_join(tid1, NULL); pthread_join(tid1, NULL); return 0; }