在單線程程序中,我們經常要用到"全局變量"以實現多個函數間共享數據, 然而在多線程環境下,由於數據空間是共享的,因此全局變量也為所有線程所共有。但有時應用程序設計中有必要提供線程私有的全局變量,僅在某個線程中有效,但卻可以跨多個函數訪問。POSIX線程庫通過維護一定的數據結構來解決這個問題,這個些數據稱為(Thread-specific-data或 TSD)。
相關函數如下:
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)); int pthread_key_delete(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *pointer); void * pthread_getspecific(pthread_key_t key); pthread_once_t once_control = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
從上圖可知:當調用pthread_key_create 後會產生一個所有線程都可見的線程特定數據(TSD)的鍵值(如上圖中所有的線程都會得到一個pkey[1]的值), 但是這個鍵所指向的真實數據卻是不同的,雖然都是pkey[1], 但是他們並不是指向同一塊內存,而是指向了只屬於自己的實際數據, 因此, 如果線程0更改了pkey[1]所指向的數據, 而並不能夠影像到線程n;
在線程調用pthread_setspecific後會將每個線程的特定數據與thread_key_t綁定起來,雖然只有一個pthread_key_t,但每個線程的特定數據是獨立的內存空間,當線程退出時會執行destructor 函數。
/** 示例1:運用pthread_once, 讓key只初始化一次 注意: 將對key的初始化放入到init_routine中 **/ pthread_key_t key; pthread_once_t once_control = PTHREAD_ONCE_INIT; typedef struct Tsd { pthread_t tid; char *str; } tsd_t; //線程特定數據銷毀函數, //用來銷毀每個線程所指向的實際數據 void destructor_function(void *value) { free(value); cout << "destructor ..." << endl; } //初始化函數, 將對key的初始化放入該函數中, //可以保證inti_routine函數只運行一次 void init_routine() { pthread_key_create(&key, destructor_function); cout << "init..." << endl; } void *thread_routine(void *args) { pthread_once(&once_control, init_routine); //設置線程特定數據 tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t)); value->tid = pthread_self(); value->str = (char *)args; pthread_setspecific(key, value); printf("%s setspecific, address: %p\n", (char *)args, value); //獲取線程特定數據 value = (tsd_t *)pthread_getspecific(key); printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str); sleep(2); //再次獲取線程特定數據 value = (tsd_t *)pthread_getspecific(key); printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str); pthread_exit(NULL); } int main() { pthread_t tid1, tid2; pthread_create(&tid1, NULL, thread_routine, (void *)"thread1"); pthread_create(&tid2, NULL, thread_routine, (void *)"thread2"); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_key_delete(key); return 0; }運行結果如下:
init....
thread1 setspecific ,address: 0x7fe7a00008c0
tid: 0xa8192700, str = thread1
thread2 setspecific ,address :0x7fe7980008c0
tid: 0xa7991700 ,str = thread2
tid: 0xa8192700 ,str = thread1
tid: 0xa7001700 ,str = thread2
destructor...
destructor...
主線程創建了兩個線程然後join 等待他們退出;給每個線程的執行函數都是thread_routine,thread_routine 中調用了pthread_once,此函數表示如果當第一個線程調用它時會執行once_routine,然後從once_routine返回即pthread_once 返回,而接下去的其他線程調用它時將不再執行once_routine,此舉是為了只調用pthread_key_create 一次,即產生一個pthread_key_t 值。
在thread_routine 函數中自定義了線程特定數據的類型,對於不同的線程來說TSD的內容不同,假設線程1在第一次打印完進入睡眠的時候,線程2也開始執行並調用pthread_setspecific 綁定線程2的TSD 和key_t,此時線程1調用pthread_getspecific 返回key_t 綁定的TSD指針,仍然是線程1的TSD指針,即雖然key_t 只有一個,但每個線程都有自己的TSD。
特定數據;具有128項,通過key-value實現,一個線程創建一個key,其他線程也會創建,但是並不是指向的同一快內存,他們指向自己的數據,
這就是線程特定數據。
上述代碼中,即使是Sleeep(2),線程1的數據並不會被線程2的數據所影響,因為是線程私有的。
當線程退出的時候會銷毀2次,因為創建了兩個線程。
其中tid 是線程的id,str 是傳遞給thread_routine 的參數,可以看到有兩個不同的ptr,且destroy 調用兩次。