[TOC]
文章参考:https://www.cnblogs.com/zhangxuan/p/6515264.html
在多线程的环境下,进程内的所有线程共享进程的数据空间。因此全局变量为所有线程共享。在程序设计中有时需要保存线程自己的全局变量,这种特殊的变量仅在线程内部有效。
如常见的errno,它返回标准的错误码。errno不应该是一个局部变量。几乎每个函数都应该可以访问他,但他又不能作为是一个全局变量。否则在一个线程里输出的很可能是另一个线程的
出错信息,这个问题可以通过创建线程的私有数据(TSD thread specific data)来解决。在线程内部,私有数据可以被各个函数访问。但他对其他线程是屏蔽的。
线程私有数据采用了一键多值的技术,即一个键对应多个值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。
1 int pthread_key_create (pthread_key_t *key, void (*destructor)(void *)) ;
第一个参数为指向一个键值的指针,
第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。
key一旦被创建,所有线程都可以访问它,但各线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量,一键多值。
一键多值靠的是一个关键数据结构数组即TSD池,创建一个TSD就相当于将结构数组中的某一项设置为“in_use”,并将其索引返回给*key,然后设置清理函数。
具体使用方法 1、创建一个键
2、为一个键设置线程私有数据
3、从一个键读取线程私有数据void *pthread_getspecific(pthread_key_t
key );
4、线程退出(退出时,会调用destructor释放分配的缓存,参数是key所关联的数据)
5、删除一个键
1 2 3 int pthread_setspecific (pthread_key_t key,const void *pointer) ) ;void *pthread_getspecific (pthread_key_t key) ;
set是把一个变量的地址告诉key,一般放在变量定义之后,get会把这个地址读出来,然后你自己转义成相应的类型再去操作,注意变量的有效期。 只不过,在不同的线程里可以操作同一个key,他们不会冲突,比如线程a,b,c set同样的key,分别get得到的地址会是之前各自传进去的值。 这样做的意义在于,可以写一份线程代码,通过key的方式多线程操作不同的数据。
1 int pthread_setspecific (pthread_key_t key, const void *value) ;
该函数将value的值(不是内容)与key相关联。用pthread_setspecific为一个键指定新的线程数据时,线程必须先释放原有的线程数据用以回收空间。
1 int pthread_key_delete (pthread_key_t key) ;
用来删除一个键,删除后,键所占用的内存将被释放。注销一个TSD,这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function)
而只是将TSD释放以供下一次调用pthread_key_create()使用。需要注意的是,键占用的内存被释放。与该键关联的线程数据所占用的内存并不被释放。因此,线程数据的释放,必须在释放键之前完成。
简单的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <pthread.h> #include <stdio.h> pthread_key_t key;pthread_t thid1;pthread_t thid2;void * thread2 (void * arg) { printf ("thread:%lu is running\n" , pthread_self ()); int key_va = 3 ; pthread_setspecific (key, (void *)key_va); printf ("thread:%lu return %d\n" , pthread_self (), (int )pthread_getspecific (key)); } void * thread1 (void * arg) { printf ("thread:%lu is running\n" , pthread_self ()); int key_va = 5 ; pthread_setspecific (key, (void *)key_va); pthread_create (&thid2, NULL , thread2, NULL ); printf ("thread:%lu return %d\n" , pthread_self (), (int )pthread_getspecific (key)); } int main () { printf ("main thread:%lu is running\n" , pthread_self ()); pthread_key_create (&key, NULL ); pthread_create (&thid1, NULL , thread1, NULL ); pthread_join (thid1, NULL ); pthread_join (thid2, NULL ); int key_va = 1 ; pthread_setspecific (key, (void *)key_va); printf ("thread:%lu return %d\n" , pthread_self (), (int )pthread_getspecific (key)); pthread_key_delete (key); printf ("main thread exit\n" ); return 0 ; }
释放空间、每次设置之前判断的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> static pthread_key_t str_key;static pthread_once_t str_alloc_key_once=PTHREAD_ONCE_INIT;static void str_alloc_key () ;static void str_alloc_destroy_accu (void * accu) ;char * str_accumulate (const char * s) { char * accu; pthread_once (&str_alloc_key_once,str_alloc_key); accu=(char *)pthread_getspecific (str_key); if (accu==NULL ) { accu=malloc (1024 ); if (accu==NULL ) return NULL ; accu[0 ]=0 ; pthread_setspecific (str_key,(void *)accu); printf ("Thread %lx: allocating buffer at %p\n" ,pthread_self (),accu); } strcat (accu,s); return accu; } static void str_alloc_key () { pthread_key_create (&str_key,str_alloc_destroy_accu); printf ("Thread %lx: allocated key %d\n" ,pthread_self (),str_key); } static void str_alloc_destroy_accu (void * accu) { printf ("Thread %lx: freeing buffer at %p\n" ,pthread_self (),accu); free (accu); } void * process (void *arg) { char * res; res=str_accumulate ("Resule of " ); if (strcmp ((char *)arg,"first" )==0 ) sleep (3 ); res=str_accumulate ((char *)arg); res=str_accumulate (" thread" ); printf ("Thread %lx: \"%s\"\n" ,pthread_self (),res); return NULL ; } int main (int argc,char * argv[]) { char * res; pthread_t th1,th2; res=str_accumulate ("Result of " ); pthread_create (&th1,NULL ,process,(void *)"first" ); pthread_create (&th2,NULL ,process,(void *)"second" ); res=str_accumulate ("initial thread" ); printf ("Thread %lx: \"%s\"\n" ,pthread_self (),res); pthread_join (th1,NULL ); pthread_join (th2,NULL ); pthread_exit (0 ); }