pthread線程特定數據
線程特定數據,也被稱為線程私有數據,是一種存儲和查找一個特定線程相關數據的機制。我們稱這個數據為線程特定或線程私有的原因,是因為每個線程訪問它自己獨立的數據拷貝,而不用擔心和其它線程的訪問的同步。
線程特定數據看似很復雜,其實我們可以把它理解為就是一個索引和指針。key結構中存儲的是索引,pthread結構中存儲的是指針,指向線程中的私有數據,通常是malloc函數返回的指針。
POSIX要求實現POSIX的系統為每個進程維護一個稱之為Key的結構數組(如圖1所示),這個數組中的每個結構稱之為一個線程特定數據元素。POSIX規定系統實現的Key結構數組必須包含不少於128個線程特定元素,而每個線程特定數據元素至少包含兩項內容:使用標誌和析構函數指針。key結構中的標誌指示這個數組元素是否使用,所有的標誌初始化為“不在使用”。
當一個線程調用pthread_key_create創建一個新的線程特定數據元素時,系統搜索其所在進程的Key結構數組,找出其中第一個未使用的元素,並通過keyptr返回該元素的鍵,即就是我們前面說的索引。pthread_key_create函數的第二個參數destructor是一個函數指針,指向一個析構函數,用於線程結束以後的一些後期後期處理工作,析構函數的額參數就是線程特定數據的指針。
除了進程範圍內地的key結構數組外,系統還在進程中維護關於每個線程的線程結構,把這個特定於線程的結構稱為pthread結構,它的部分內容是於key數組對應的指針數組(如圖2所示),它的128個指針和進程中的128個可能的鍵(索引)是逐一關聯的。指針指向的內存就是線程特有數據。
下面看一個具體的過程,啟動一個進程並創建了若幹線程,其中一個線程(比如線程1),要申請線程私有數據,系統調用pthread_key_creat()在圖1所示的key結構數組中找到第一個未用的元素,並把它的鍵,也就是看面說的索引(0-127),返回給調用者,假設返回的索引是1,線程之後通過pthrea_getspecific()調用獲得本線程的pkey[1]值,返回的是一個空指針ptr = null,這個指針就是我們可以通過索引1使用的線程數據的首地址了,但是他現在為空,因此根據實際情況用malloc分配一快內存,在使用pthread_setspecific()調用將特定數據的指針指向剛才分配到內存區域。整個過程結束後key結構和pthread結構如圖3所示,
在操作線程特定數據時涉及到的函數調用有一下幾個:
#include <pthread.h> //成功返回0,失敗返回錯誤號。 int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *)); //返回線程特定數據,或者如果沒有值關聯到這個關鍵字時返回NULL。 void *pthread_getspecific(pthread_key_t key); //成功返回0,失敗返回錯誤號。 int pthread_setspecific(pthread_key_t key, const void *value); pthread_once_t initflag = PTHREAD_ONCE_INIT; //成功返回0,失敗返回錯誤碼。 int pthread_once(pthread_once_t *initflag, void (*initfn)(void)); //成功返回0,失敗返回錯誤號。 int pthread_key_delete(pthread_key_t *key);
線程特定數據的典型用法如下:
void destructor(void *) pthread_key_t key; pthread_once_t init_done = PTHREAD_ONCE_INIT; void thread_init(void) { err = pthread_key_create(&key, destructor); } int threadfunc(void *arg) { pthread_once(&init_done, thread_init); if( (ptr = pthread_getspecific(key)) == NULL ){ ptr = malloc(len); pthread_setspecific(key,ptr); ... } ... }
當調用pthread_key_create 後會產生一個所有線程都可見的線程特定數據(TSD)的鍵值(如上圖中所有的線程都會得到一個pkey[1]的值), 但是這個鍵所指向的真實數據卻是不同的,雖然都是pkey[1], 但是他們並不是指向同一塊內存,而是指向了只屬於自己的實際數據, 因此, 如果線程0更改了pkey[1]所指向的數據, 而並不能夠影像到線程n;
在線程調用pthread_setspecific後會將每個線程的特定數據與thread_key_t綁定起來,雖然只有一個pthread_key_t,但每個線程的特定數據是獨立的內存空間,當線程退出時會執行destructor 函數。
/** 示例1: 設置/獲取線程特定數據 在兩個線程中分別設置/獲取線程特定數據, 查看兩個線程中的數據是否是一樣的(肯定是不一樣的O(∩_∩)O~) **/ pthread_key_t key; typedef struct Tsd { pthread_t tid; char *str; } tsd_t; //用來銷毀每個線程所指向的實際數據 void destructor_function(void *value) { free(value); cout << "destructor ..." << endl; } void *thread_routine(void *args) { //設置線程特定數據 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() { //這樣每個線程當中都會有一個key可用了, //但是每個key所綁定的實際區域需要每個線程自己指定 pthread_key_create(&key, destructor_function); 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; }
/** 示例2:運用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; }
https://blog.csdn.net/zjf280441589/article/details/43883033
http://www.360doc.com/content/18/0617/11/56818466_763036051.shtml
pthread線程特定數據