您的位置:首页 > 编程语言

《UNIX环境高级编程》笔记--线程私有数据

2014-01-12 12:47 274 查看
线程私有数据时存储和查询与某个线程相关的数据的一种机制。把这种数据成为线程私有数据或线程特定数据的原因是,

希望每个线程都可以独立地访问数据副本,并不需要担心与其他线程的同步访问问题。

为什么要使用线程私有数据呢?原因有两个:

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_key_delete并不会激活与键相关的析构函数。

键一旦创建,就可以用过调用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;
}
这个函数根据环境变量的名字获取名字对应的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: