參照:http://blog.csdn.net/xiaohuangcat/article/details/18267561
在多線程的環境下,進程內的所有線程共享進程的數據空間。因此全局變量為所有線程共享。在程序設計中有時需要保存線程自己的全局變量,這種特殊的變量僅在線程內部有效。
如常見的errno,它返回標准的錯誤碼。errno不應該是一個局部變量。幾乎每個函數都應該可以訪問他,但他又不能作為是一個全局變量。否則在一個線程里輸出的很可能是另一個線程的
出錯信息,這個問題可以通過創建線程的私有數據(TSD thread specific data)來解決。在線程內部,私有數據可以被各個函數訪問。但他對其他線程是屏蔽的。
線程私有數據采用了一鍵多值的技術,即一個鍵對應多個值。訪問數據時都是通過鍵值來訪問,好像是對一個變量進行訪問,其實是在訪問不同的數據。
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、刪除一個鍵
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的方式多線程操作不同的數據。
int pthread_setspecific(pthread_key_t key, const void *value);該函數將value的值(不是內容)與key相關聯。用pthread_setspecific為一個鍵指定新的線程數據時,線程必須先釋放原有的線程數據用以回收空間。
nt pthread_key_delete(pthread_key_t key);用來刪除一個鍵,刪除后,鍵所占用的內存將被釋放。注銷一個TSD,這個函數並不檢查當前是否有線程正使用該TSD,也不會調用清理函數(destr_function),
而只是將TSD釋放以供下一次調用pthread_key_create()使用。需要注意的是,鍵占用的內存被釋放。與該鍵關聯的線程數據所占用的內存並不被釋放。因此,線程數據的釋放,必須在釋放鍵之前完成。
簡單的示例代碼:
#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; }
釋放空間、每次設置之前判斷的代碼:
/*三個線程:主線程,th1,th2各自有自己的私有數據區域 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> static pthread_key_t str_key; //define a static variable that only be allocated once 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);//str_alloc_key()這個函數只調用一次 accu=(char*)pthread_getspecific(str_key);//取得該線程對應的關鍵字所關聯的私有數據空間首址 if(accu==NULL)//每個新剛創建的線程這個值一定是NULL(沒有指向任何已分配的數據空間) { accu=malloc(1024);//用上面取得的值指向新分配的空間 if(accu==NULL) return NULL; accu[0]=0;//為后面strcat()作准備 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);/*創建關鍵字及其對應的內存釋放函數,當進程創建關鍵字后,這個關鍵字是NULL。之后每創建一個線程os都會分給一個對應的關鍵字,關鍵字關聯線程私有數據空間首址,初始化時是NULL*/ printf("Thread %lx: allocated key %d\n",pthread_self(),str_key); } /*線程退出時釋放私有數據空間,注意主線程必須調用pthread_exit()(調用exit()不行)才能執行該函數釋放accu指向的空間*/ 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); }