版权声明:本文为博主原创文章,未经博主允许不得转载。
线程私有数据时存储和查询与某个线程相关的数据的一种机制。把这种数据成为线程私有数据或线程特定数据的原因是,
希望每个线程都可以独立地访问数据副本,并不需要担心与其他线程的同步访问问题。
为什么要使用线程私有数据呢?原因有两个:
1.有时候需要维护基于每个线程的数据,因为线程ID并不能保证是小而连续的整数,所以不能简单地分配一个线程数据
数组,用线程ID作为数组的索引。即使线程ID确实是小而连续的整数,可能还希望有一些额外的保护,以防止某个线程的数
据与其他线程数据相混淆。
2.它提供了让基于进程的接口适应多线程环境的机制。一个很明显的实例就是errno,在多线程环境下,errno不能简单地
定义为一个全局变量,因为多个线程可能使用在读写该变量。为了让线程能够使用那些原本基于进程的线程调用和库例程,
errno被重新定义为线程私有数据。
在分配线程私有数据之前,需要创建于该数据关联的键,这个键将用于获取对线程私有数据的访问权,使用
pthread_key_create创建一个键。
- #include<pthread.h>
- int pthread_key_create(pthread_key_t *keyp, void (*destructor) (void*));
- //成功返回0,否则返回错误编号。
创建的键存放在keyp指向的内存单元,这个键额可以被进程中的所有线程使用,但每个线程把这个键与不同的线程私有数据
地址进行关联。创建新键时,每个线程的数据地址设为null值。
除了创建键以外,pthread_key_create可以选择为该键关联析构函数,当线程退出时,如果数据地址已经被设置为非null,那
么析构函数就会被调用,它唯一的参数就是该数据地址。如果传入的destructor参数为null,就表明没有析构函数与键关联。
当线程调用pthread_exit或者线程执行返回,正常退出时,析构函数就会被调用,但是如果线程调用了exit、_exit、_Exit、abort
或者出现其他非正常的退出时,就不会调用析构函数。
线程通常使用malloc为线程私有数据分配内存空间,析构函数通常释放以分配的内存,如果线程没有释放内存就退出了,那么
这块内存就会泄露。
如果多个线程都调用了pthread_key_create函数, 那么有些线程可能看到某个键值,而其他的线程看到的可能是另一个不
同的线程,解决这种竞争的方法是使用pthread_once.
- #include<pthread.h>
- pthread_once_t initflag = PTHREAD_ONCE_INIT;
- int pthread_once(pthread_once_t *initflag, void(*initfn)(void));
- //成功则返回0,否则返回错误编号。
initflag必须是一个非本地变量(全局变量或静态变量),必须初始化为PTHREAD_ONCE_INIT.如果每个线程都调用
pthread_once,系统就能保证初始化例程initfn只被调用一次。
对所有的线程,都可以通过调用pthread_key_delete来取消键与线程私有数据之间的关联关系。
- #include<pthread.h>
- int pthread_key_delete(pthread_key_t *key);
- //成功则返回0,否则返回错误编号。
键一旦创建,就可以用过调用pthread_setspecific函数把键和线程私有数据关联起来,可以通过pthread_getspecific函数
获取线程私有数据的地址。
- #include<pthread.h>
- void *pthread_getspecific(pthread_key_t key); //返回线程私有数据值,没有值与键关联则返回NULL。
- int pthread_setspecific(pthread_key_t key, const void *value); //成功则返回0,否则返回错误编号。
实践:
- #include<limits.h>
- #include<string.h>
- #include<pthread.h>
- #include<stdlib.h>
- static pthread_key_t key;
- static pthread_once_t init_done = PTHREAD_ONCE_INIT;
- pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;
- extern char **environ;
- static void thread_init(void){
- pthread_key_create(&key, free);
- }
- char* getenv(const char *name){
- int i, len;
- char *envbuf;
- pthread_once(*init_done, thread_init);
- pthread_mutex_lock(&env_mutex);
- envbuf = (char*)pthread_getspecific(key);
- if(envbuf == NULL){
- envbuf = malloc(ARG_MAX);
- if(envbuf == NULL){
- pthread_mutex_unlock(&env_mutex);
- return NULL;
- }
- pthread_setpecific(key,envbuf);
- }
- len = str;en(name);
- for(i=0; environ[i] != NULL; i++){
- if((!strncmp(name,environ[i],len) == 0) && (environ[i][len] == '=')){
- strcpy(envbuf, &environ[i][len+1]);
- phread_mutex_unlock(&env_mutex);
- return envbuf;
- }
- }
- pthread_mutex_unlock(&env_mutex);
- return NULL;
- }