1. 程式人生 > >pthread線程特定數據

pthread線程特定數據

過程 ESS 第一個 img tsp add 可能 現在 color

線程特定數據,也被稱為線程私有數據,是一種存儲和查找一個特定線程相關數據的機制。我們稱這個數據為線程特定或線程私有的原因,是因為每個線程訪問它自己獨立的數據拷貝,而不用擔心和其它線程的訪問的同步。

線程特定數據看似很復雜,其實我們可以把它理解為就是一個索引指針。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線程特定數據