歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

Linux多線程實踐(四 )線程的特定數據

在單線程程序中,我們經常要用到"全局變量"以實現多個函數間共享數據, 然而在多線程環境下,由於數據空間是共享的,因此全局變量也為所有線程所共有。但有時應用程序設計中有必要提供線程私有的全局變量,僅在某個線程中有效,但卻可以跨多個函數訪問。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 調用兩次。
Copyright © Linux教程網 All Rights Reserved