UNIX环境高级编程——线程私有数据
来源:互联网 发布:js检测类型 编辑:程序博客网 时间:2024/05/22 14:34
线程私有数据(Thread-specific data,TSD):存储和查询与某个线程相关数据的一种机制。
进程中的所有线程都可以访问进程的整个地址空间,除非使用寄存器(一个线程真正拥有的唯一私有存储是处理器寄存器),线程没有办法阻止其它线程访问它的数据,线程私有数据也不例外,但是管理线程私有数据的函数可以提高线程间的数据独立性。
进程内的所有线程共享进程的数据空间,因此全局变量为所有线程所共有。但有时线程也需要保存自己的私有数据,这时可以创建线程私有数据(Thread-specific Date)TSD来解决。在线程内部,私有数据可以被各个函数访问,但对其他线程是屏蔽的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量。(即在线程里面是全局变量)
创建线程私有数据就是为了线程内部各个函数可以很容易的传递数据信息,因此要使线程外的函数不能访问这些数据,而线程内的函数使用这些数据就像线程内的全局变量一样,这些数据在一个线程内部是全局的,一般用线程私有数据的地址作为线程内各个函数访问该数据的入口。
线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个数值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。使用线程私有数据时,首先要为每个线程私有数据创建一个相关联的键。在各个线程内部,都使用这个公用的键来指代线程数据,但是在不同的线程中,这个键代表的数据是不同的。操作线程私有数据的函数主要有4个:pthread_key_create(创建一个键),pthread_setspecific(为一个键设置线程私有数据),pthread_getspecific(从一个键读取线程私有数据),pthread_key_delete(删除一个键)。
创建一个键:
如果创建一个线程私有数据键,必须保证pthread_key_create对于每个Pthread_key_t变量仅仅被调用一次,因为如果一个键被创建两次,其实是在创建两个不同的键,第二个键将覆盖第一个键,第一个键以及任何线程可能为其关联的线程私有数据值将丢失。
创建新键时,每个线程的私有数据地址设为NULL。
注意:创建的键存放在keyp指向的内存单元,这个键可以被进程中的所有线程使用,但每个线程把这个键与不同的线程私有数据地址进行关联。
除了创建键以外,pthread_key_create可以选择为该键关联析构函数,当线程退出时,如果线程私有数据地址被置为非NULL值,那么析构函数就会被调用。
注意:析构函数参数为退出线程的私有数据的地址。如果私有数据的地址为NULL,就说明没有析构函数与键关联即不需要调用该析构函数。
当线程调用pthread_exit或者线程执行返回,正常退出时,析构函数就会被调用,但是如果线程调用了exit、_exit、Exit函数或者abort或者其它非正常退出时,就不会调用析构函数。
线程通常使用malloc为线程私有数据分配空间,析构函数通常释放已分配的线程私有数据的内存。
线程可以为线程私有数据分配多个键,每个键都可以有一个析构函数与它关联。各个键的析构函数可以互不相同,当然它们也可以使用相同的析构函数。
线程退出时,线程私有数据的析构函数将按照操作系统实现定义的顺序被调用。析构函数可能调用另外一个函数,而该函数可能创建新的线程私有数据而且把这个线程私有数据和当前的键关联起来。当所有的析构函数都调用完成以后,系统会检查是否有非NULL的线程私有数据值与键关联,如果有的话,再次调用析构函数,这个过程一直重复到线程所有的键都为NULL值线程私有数据,或者已经做了PTHREAD_DESTRUCTOR_ITERATIONS中定义的最大次数的尝试。
取消键与线程私有数据之间的关联:
注意:对于每个pthread_key_t变量(即键)必须仅调用一次pthread_key_create。如果一个键创建两次,其实是在创建不同的键,第二个键将覆盖第一个,第一个键与任何线程可能为其设置的值将一起永远的丢失。所以,pthread_key_create放在主函数中执行;或每个线程使用pthread_once来创建键。
线程私有数据与键关联:
void* pthread_getspecific(pthread_key_t key);//返回值:线程私有数据地址;若没有值与键关联则返回NULL
如果没有线程私有数据值与键关联,pthread_getspecific键返回NULL,可以依据此来确定是否调用pthread_setspecific。
注意:两个线程对自己的私有数据操作是互相不影响的。也就是说,虽然 key 是同名且全局,但访问的内存空间并不是相同的一个。key 就像是一个数据管理员,线程的私有数据只是到他那去注册,让它知道你这个数据的存在
示例代码:
#include <pthread.h>
#include <stdlib.h>
typedef struct private_tag {
pthread_t thread_id;
char *string;
} private_t;
pthread_key_t identity_key; /* Thread-specific data key */
pthread_mutex_t identity_key_mutex = PTHREAD_MUTEX_INITIALIZER;
long identity_key_counter = 0;
void identity_key_destructor (void*value)
{
private_t *private= (private_t*)value;
int status;
printf ("thread \"%s\" exiting...\n", private->string);
free (value);
status = pthread_mutex_lock (&identity_key_mutex);
if (status != 0)
perror("pthread_mutex_lock");
identity_key_counter--;
if (identity_key_counter <= 0) {
status = pthread_key_delete (identity_key);
if (status != 0)
perror("pthread_key_delete");
printf ("key deleted...\n");
}
status = pthread_mutex_unlock (&identity_key_mutex);
if (status != 0)
perror("pthread_mutex_unlock");
}
void *identity_key_get (void)
{
void *value;
int status;
value = pthread_getspecific (identity_key);
if (value == NULL) {
value = malloc (sizeof (private_t));
if (value == NULL)
perror ("malloc");
status = pthread_setspecific (identity_key, (void*)value);
if (status != 0)
perror("pthread_setspecific");
}
return value;
}
void *thread_routine (void *arg)
{
private_t *value;
value = (private_t*)identity_key_get ();
value->thread_id= pthread_self ();
value->string= (char*)arg;
printf ("thread \"%s\" starting...\n", value->string);
sleep (2);
return NULL;
}
void main (int argc,char *argv[])
{
pthread_t thread_1, thread_2;
private_t *value;
int status;
status = pthread_key_create (&identity_key, identity_key_destructor);
if (status != 0)
perror("pthread_key_create");
identity_key_counter = 3;
value = (private_t*)identity_key_get ();
value->thread_id= pthread_self ();
value->string= "Main thread";
status = pthread_create (&thread_1, NULL,thread_routine,"Thread 1");
if (status != 0)
perror("pthread_create");
status = pthread_create (&thread_2, NULL,thread_routine,"Thread 2");
if (status != 0)
perror("pthread_create");
pthread_exit (NULL);
}
huangcheng@ubuntu:~$ ./a.out
thread "Main thread" exiting...
thread "Thread 2" starting...
thread "Thread 1" starting...
thread "Thread 2" exiting...
thread "Thread 1" exiting...
key deleted...
huangcheng@ubuntu:~$
示例代码2:
#include <pthread.h>
#include <stdlib.h>
typedef struct tsd_tag{
pthread_t thread_id;
char *string;
}tsd_t;
pthread_key_t key;
pthread_once_t once = PTHREAD_ONCE_INIT;
void once_routine(void)
{
int status;
printf("Initializing key\n");
status = pthread_key_create(&key, NULL);
if(status != 0){
perror("pthread_key_create");
}
}
void *thread_routine(void *arg)
{
int status;
tsd_t *value = NULL;
status = pthread_once(&once, once_routine);
if(status != 0){
perror("pthread_once");
}
value = (tsd_t *)malloc(sizeof(tsd_t));
if(value == NULL){
perror("malloc");
}
status = pthread_setspecific(key, (void*)value);
if(status != 0){
perror("pthread_setspecific");
}
printf("%s set tsd value at %p\n", (char*)arg, value);
value->thread_id= pthread_self();
value->string= (char*)arg;
printf("%s starting......\n", (char*)arg);
sleep(2);
value = (tsd_t *)pthread_getspecific(key);
if(value == NULL){
printf("no thread-specific data value was associated \
with key\n");
pthread_exit(NULL);
}
printf("%s done......\n", value->string);
}
int main(int argc,char **argv)
{
pthread_t thread1, thread2;
int status;
status = pthread_create(&thread1, NULL, thread_routine,"thread 1");
if(status != 0){
perror("pthread_create");
}
status = pthread_create(&thread2, NULL, thread_routine,"thread 2");
if(status != 0){
perror("pthread_create");
}
pthread_exit(NULL);
}
运行结果:
huangcheng@ubuntu:~$ ./a.out
Initializing key
thread 2 set tsd value at 0x8fb7520
thread 2 starting......
thread 1 set tsd value at 0x8fb7530
thread 1 starting......
thread 2 done......
thread 1 done......
示例代码3:
#include <stdlib.h>
#include <pthread.h>
pthread_key_t key;
struct test_struct {
int i;
float k;
};
void *child1 (void *arg)
{
struct test_struct struct_data;
struct_data.i = 10;
struct_data.k = 3.1415;
pthread_setspecific (key, &struct_data);
printf ("结构体struct_data的地址为 0x%p\n", &(struct_data));
printf ("child1 中 pthread_getspecific(key)返回的指针为:0x%p\n", (struct test_struct*)pthread_getspecific(key));
printf ("利用 pthread_getspecific(key)打印 child1 线程中与key关联的结构体中成员值:\nstruct_data.i:%d\nstruct_data.k: %f\n", ((struct test_struct*)pthread_getspecific (key))->i, ((struct test_struct*)pthread_getspecific(key))->k);
printf ("------------------------------------------------------\n");
}
void *child2 (void *arg)
{
int temp = 20;
sleep (2);
printf ("child2 中变量 temp 的地址为 0x%p\n", &temp);
pthread_setspecific (key, &temp);
printf ("child2 中 pthread_getspecific(key)返回的指针为:0x%p\n", (int*)pthread_getspecific(key));
printf ("利用 pthread_getspecific(key)打印 child2 线程中与key关联的整型变量temp 值:%d\n",*((int*)pthread_getspecific(key)));
}
int main (void)
{
pthread_t tid1, tid2;
pthread_key_create (&key, NULL);
pthread_create (&tid1, NULL, (void*)child1, NULL);
pthread_create (&tid2, NULL, (void*)child2, NULL);
pthread_join (tid1, NULL);
pthread_join (tid2, NULL);
pthread_key_delete (key);
return (0);
}
运行结果:
huangcheng@ubuntu:~$ ./a.out
结构体struct_data的地址为 0x0xb77db388
child1 中 pthread_getspecific(key)返回的指针为:0x0xb77db388
利用 pthread_getspecific(key)打印 child1 线程中与key关联的结构体中成员值:
struct_data.i:10
struct_data.k: 3.141500
------------------------------------------------------
child2 中变量 temp 的地址为 0x0xb6fda38c
child2 中 pthread_getspecific(key)返回的指针为:0x0xb6fda38c
利用 pthread_getspecific(key)打印 child2 线程中与key关联的整型变量temp 值:20
- UNIX环境高级编程——线程私有数据
- UNIX环境高级编程——线程私有数据
- unix 环境高级编程 线程私有数据
- 《unix高级环境编程》线程控制——线程私有数据
- 《unix高级环境编程》线程控制——线程私有数据
- 《UNIX环境高级编程》笔记--线程私有数据
- UNIX环境高级编程——线程
- UNIX环境高级编程——线程
- UNIX环境高级编程——线程
- UNIX环境高级编程——线程限制 && 线程属性
- 《unix高级环境编程》线程——线程基本概述
- 《unix高级环境编程》线程——线程终止
- 《unix高级环境编程》线程——线程同步
- 《unix高级环境编程》线程控制——线程属性
- 《unix高级环境编程》线程——线程基本概述
- 《unix高级环境编程》线程——线程终止
- 《unix高级环境编程》线程——线程同步
- 《unix高级环境编程》线程控制——线程属性
- HTTP和TCP的区别和联系
- hdu 2955 Robberies(01背包变形)
- 几种发行版中的包管理器
- sql getdate() 时间格式设置
- hdu 1114 Piggy-Bank
- UNIX环境高级编程——线程私有数据
- 关于Struts2通配符的问题
- 【rmzt】阳光&&美女win7主题
- I,P,B帧和PTS,DTS的关系
- 伪代码
- Android FragmentManage FragmentTransaction介绍
- linux进程间通信
- Visual Studio程序员箴言
- 零死角玩转STM32---阅读摘记三