分类: LINUX
2011-12-12 17:31:50
C 程序有两类基本数据:局部数据和全局数据。
对于多线程 C 程序,添加了第三类数据:线程特定数据。线程特定数据与全局数据非常相似,区别在于前者为线程专有。
线程特定数据基于每线程进行维护。TSD(特定于线程的数据)是定义和引用线程专用数据的唯一方法。每个线程特定数据项都与一个作用于进程内所有线程的键关联。通过使用 key,线程可以访问基于每线程进行维护的指针
int pthread_key_create(pthread_key_t *key, void (*destructor) (void *))
pthread_key_t key;
int ret;
/* key create without destructor */
ret = pthread_key_create(&key, NULL);
/* key create with destructor */
ret = pthread_key_create(&key, destructor);
可以使用 pthread_key_create函数分配用于标识进程中线程特定数据的键。键对进程中的所有线程来说是全局的。创建线程特定数据时,所有线程最初都具有与该键关联的 NULL 值。
创建键之后,每个线程都会将一个值绑定到该键。这些值特定于线程并且针对每个线程单独维护。如果创建该键时指定了 destructor 函数,则该线程终止时,系统会解除针对每线程的绑定。
使用可选的析构函数 destructor 可以释放过时的存储。如果某个键具有非 NULL destructor 函数,而线程具有一个与该键关联的非 NULL 值,则该线程退出时,系统将使用当前的相关值调用 destructor 函数。destructor 函数的调用顺序不确定。
pthread_key_create 返回值pthread_key_create() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,pthread_key_create() 将失败并返回相应的值。
EAGAIN
描述: key 名称空间已经用完。
ENOMEM
描述: 此进程中虚拟内存不足,无法创建新键。
删除线程特定数据键使用 pthread_key_delete函数 可以销毁现有线程特定数据键。由于键已经无效,因此将释放与该键关联的所有内存。引用无效键将返回错误。
语法int pthread_key_delete(pthread_key_t key);
#include
pthread_key_t key;
int ret;
/* key previously created */
ret = pthread_key_delete(key);
如果已删除键,则使用调用 pthread_setspecific() 或 pthread_getspecific() 引用该键时,生成的结果将是不确定的。
程序员在调用删除函数之前必须释放所有线程特定资源。删除函数不会调用任何析构函数。反复调用 pthread_key_create() 和 pthread_key_delete() 可能会产生问题。
对于每个所需的键,应当只调用 pthread_key_create() 一次。
pthread_key_delete() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下情况,pthread_key_delete() 将失败并返回相应的值。
EINVAL
描述: key 的值无效。
设置线程特定数据使用 pthread_setspecific() 可以为指定线程特定数据键设置线程特定绑定。
语法
int pthread_setspecific(pthread_key_t key, const void *value);
#include
pthread_key_t key;
void *value;
int ret;
/* key previously created */
ret = pthread_setspecific(key, value);
返回值pthread_setspecific() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,pthread_setspecific() 将失败并返回相应的值。
ENOMEM
描述: 虚拟内存不足。
EINVAL
描述: key 无效。
注意:设置新绑定时,pthread_setspecific() 不会释放其存储空间。必须释放现有绑定,否则会出现内存泄漏。
获取线程特定数据请使用 pthread_getspecific 获取调用线程的键绑定,并将该绑定存储在 value 指向的位置中。
语法 void *pthread_getspecific(pthread_key_t key);
#include
pthread_key_t key;
void *value;
/* key previously created */
value = pthread_getspecific(key);
返回值pthread_getspecific 不返回任何错误。
示例
#include
#include
#include
#define LEN 100
pthread_key_t key;
void A(char* s)
{
char* p = (char*)pthread_getspecific(key);
strcpy(p,s);
}
void B()
{
char* p = (char*)pthread_getspecific(key);
printf("%s\n",p);
}
void destructor(void* ptr)
{
// sleep(1);
printf("...%x\n",(char*)ptr);
free(ptr);
printf("Memorey free\n");
}
void createkey(void)
{
pthread_key_create(&key,destructor); //创建键.
}
void* threadFun1(void* arg)
{
static pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_once(&once,createkey); //为了只创建一次这个键.第一次调用pthread_once时它执行这个函数creatkey,以后的调用将忽略
char* p = (char*)malloc(LEN);
printf("%x\n",p);
pthread_setspecific(key,p);
//sleep(2);
A("Thread1");
B();
}
void* threadFun2(void* arg)
{
static pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_once(&once,createkey);
char* p = (char*)malloc(LEN);
printf("%x\n",p);
pthread_setspecific(key,p);
// A("Thread1");
A("Thread2...");
B();
}
int main()
{
pthread_t tid1,tid2;
// pthread_key_create(&key,destructor);
if(pthread_create(&tid1, NULL, &threadFun1, NULL) != 0)
printf("create thread1 fail\n");
if(pthread_create(&tid2, NULL, &threadFun2, NULL) != 0)
printf("create thread2 fail\n");
// B();
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 1;
}
注意:当用pthread_setspecific为一个键指定新的特定线程数据时,必须自己释放原有的特定线程数据以回收空间. pthread_key_delete用来删除一个键,这个键占用的内存将释放,但它只释放键占用的内存,并不释放键关联的特定线程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中注册的destructor函数.特定线程数据的释放必须在释放键之前完成.