線程私有數據實現的原理

       在維護每個線程的私有數據的時候,我們可能會想到分配一個保存線程數據的數組,用線程的ID作爲數組的索引來實現訪問,但是有一個問題是系統生成的線程ID不能保證是一個小而連續的整數,並且用數組實現的時候由於其他線程也可以訪問其數組中的數據,這樣會引起數據混亂。這時候我們可以藉助線程的私有數據來解決這個問題。

      線程私有數據實現的主要思想是:在分配線程私有數據之前,創建與該數據相關聯的健,這個鍵可以被進程中的所有線程使用,但每個線程把這個鍵與不同的線程私有數據地址進行關聯,需要說明的是每個系統支持有限數量的線程特定數據元素(下面的例子以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_once 函數確保pthread_key_create只被調用一次,這個在以後會講到)。

之後線程調用pthread_getspecific獲取本線程的pkey[1] 的值(圖(2)中鍵1所值的指針), 返回值是一個空值,線程那麼調用malloc分配內存區並初始化此內存區。 之後線程調用pthread_setspecific把對應的所創建鍵的線程特定數據指針(pkey[1]) 設置爲指向它剛剛分配的內存區。下圖指出了此時的情形。

(圖三)

 

明白了怎樣獲取線程的特定數據值,那麼如果線程終止時系統會執行什麼操作呢?

我們知道,一個線程調用pthread_key_create創建某個特定的數據元素時,所指定的參數之一便是指向牧歌析構函數的指針。當一個線程終止時,系統將掃描該線程的pkey數組,爲每個非空的pkey指針調用相應的析構函數。 相應的析構函數是存放在圖1中的Key數組中的函數指針。這是一個線程終止時其線程特定數據的釋放手段。

明白了線程私有數據的實現原理,我們就來看一下相應函數的用法:

#include<pthread.h>

int phread_once(pthread_once_t *onceptr, vid(*init)(void));

in pthread_key_create(pthread_key_t *keyptr, void(* destructor)(void *value));

注意:pthread_once 使用onceptr 參數指向的變量中的值確保init參數所指的函數在進程範圍內之被調用一次,onceptr必須是一個非本地變量(即全局變量或者靜態變量),而且必須初始化爲PTHREAD_ONCE_INIT。

這兩個函數的典型用法如下:

pthread_key_t r1_key;

pthread_once_t r1_once = PTHREAD_ONCE_INIT;

void destructor(void *ptr)

{

     free(ptr);

}

void excute_once(void)  // 確保鍵只被創建一次

{

    pthread_key_create(&r1_key, destructor);

}

int main()

{

   pthread_once(&r1_once, excute_once);

}

 

下面還有兩個設置線程私有數據和獲得線程私有數據的函數

void *pthread_getspecific(pthread_key_t key);

int pthread_setspecific(pthread_key_t key, const void *value); -- 0 返回成功  返回其它值表示出錯。


文章出處:http://blog.csdn.net/caigen1988/article/details/7901248

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章