Linux多线程实践(4) --线程特定数据
線程特定數據
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));? ?在單線程程序中,我們經常要用到"全局變量"以實現多個函數間共享數據,?然而在多線程環境下,由于數據空間是共享的,因此全局變量也為所有線程所共有。但有時應用程序設計中有必要提供線程私有的全局變量,僅在某個線程中有效,但卻可以跨多個函數訪問。POSIX線程庫通過維護一定的數據結構來解決這個問題,這個些數據稱為(Thread-specific-data或?TSD),?線程特定數據如下圖所示:
?
?
? ?從上圖可知:當調用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; }附-Linux/Unix線程私有數據實現思想:
原文連接:http://blog.csdn.net/caigen1988/article/details/7901248
? ?線程私有數據實現的主要思想是:在分配線程私有數據之前,創建與該數據相關聯的鍵,這個鍵可以被進程中的所有線程使用,但每個線程把這個鍵與不同的線程私有數據地址進行關聯,需要說明的是每個系統支持有限數量的線程特定數據元素(如:限制為128個)。那么這個鍵的實現原理是什么呢?
? ? 其實系統為每個進程維護了一個稱之為 Key 結構的結構數組,如下圖所示:(圖1)
? ?在上圖中Key?結構的“標志”指示這個數據元素是否正在使用。在剛開始時所有的標志初始化為“不在使用”。當一個線程調用pthread_key_create創建一個新的線程特定數據元素時,系統會搜索Key結構數組,找出第一個“不在使用”的元素。并把該元素的索引(0~127,稱為“鍵”)返回給調用線程。
? ?除了進程范圍內的Key結構數組之外,系統還在進程內維護了關于多個線程的多條信息。這些特定于線程的信息我們稱之為pthread結構。其中部分內容是我們稱之為pkey數組的一個128個元素的指針數組。系統維護的關于每個線程的信息結構圖如下:
?
(圖2)
? ?在上圖中,pkey數組所有元素都被初始化為空指針。這些128個指針是和進程內128個可能的鍵逐一關聯的值。
那么當我們調用pthread_key_create函數時,系統會為我們做什么呢?
????系統首先會返回給我們一個Key結構數組中第一個“未被使用”的鍵(即索引值),每個線程可以隨后通過該鍵找到對應的位置,并且為這個位置存儲一個值(指針)。?一般來說,這個指針通常是每個線程通過調用malloc來獲得的。
知道了大概的私有數據實現的原理,那么在編程中如何使用線程的特定數據呢?
? ?假設一個進程被啟動,并且多個線程被創建。?其中一個線程調用pthread_key_create。系統在Key結構數組(圖1)中找到第1個未使用的元素。并把它的索引(0~127)返回給調用者。我們假設找到的索引為1。
之后線程調用pthread_getspecific獲取本線程的pkey[1]?的值(圖(2)中鍵1所值的指針),?返回值是一個空值,線程那么調用malloc分配內存區并初始化此內存區。?之后線程調用pthread_setspecific把對應的所創建鍵的線程特定數據指針(pkey[1])?設置為指向它剛剛分配的內存區。下圖指出了此時的情形。
?
(圖3)
明白了怎樣獲取線程的特定數據值,那么如果線程終止時系統會執行什么操作呢?
? ?我們知道,一個線程調用pthread_key_create創建某個特定的數據元素時,所指定的參數之一便是指向析構函數的指針。當一個線程終止時,系統將掃描該線程的pkey數組,為每個非空的pkey指針調用相應的析構函數。?相應的析構函數是存放在圖1中的Key數組中的函數指針。這是一個線程終止時其線程特定數據的釋放手段。
總結
以上是生活随笔為你收集整理的Linux多线程实践(4) --线程特定数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 12.2.9
- 下一篇: 利用 squid 反向代理提高网站性能