線程特定數據

在多線程程序裏,我們有可能經常遇到因爲函數使用的靜態變量無法爲不同的線程保存各自的值的問題。有很多辦法可以解決,本文就來看一下通過線程特定數據來解決這樣的問題。

每個系統支持有限的線程特定數據元素。POSIX要求這個限制不小於128(每個進程)。系統爲每個進程維護一個我們稱之爲key結構的結構數組,如圖:

key結構中的標誌指示這個數據元素是否正在使用,所有的標誌初始化爲“不在使用”。當一個線程調用pthread_key_create創建一個新的線程特定數據元素時,系統會返回第一個不在使用的元素。key結構中的析構函數指針,當一個線程終止時,系統將掃描該線程的pkey數組,爲每個非空的pkey指針調用相應的析構函數。

除了進程範圍的key結構數組外,系統還在進程內維護關於每個線程的多條信息。這些特定於線程的信息我們稱之爲pthread結構,其部分內容是我們稱之爲pkey數組的一個128個元素的指針數組。如圖:

注意當我們調用pthread_key_create創建一個鍵時,系統告訴我們這個鍵。每個線程可以隨後爲該鍵存儲一個值(指針),而這個指針通常是每個線程通過malloc獲得的。

具體的函數如下:

#include <pthread.h>
int pthread_once(pthread_once_t *onceptr, void (*init)(void));
int pthread_key_create(pthread_key_t *keyptr, void (*destructor)(void *value));

void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
使用用例:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_key_t rl_key;
pthread_once_t rl_once = PTHREAD_ONCE_INIT;

void destructor(void *value) {
    printf("pthread:%d destructor value(%p)\n", pthread_self(), value);
    free(value);// 線程結束後執行
}

void create_key() {
    pthread_key_create(&rl_key, destructor);// 申請一個key
    printf("pthread:%d create_key %d\n", pthread_self(), rl_key);
}

void *foo(void *param) {
    int i;
    for(i = 0; i < 2; i++) {
    pthread_once(&rl_once, create_key); // 只運行一次create_key

    void *ptr;
    if ( (ptr = pthread_getspecific(rl_key)) == NULL) {// 檢查當前線程key域是否有值
        ptr = malloc(10);
        pthread_setspecific(rl_key, ptr);// 設置key的值
        printf("pthread:%d key(%d) value(%p)\n", pthread_self(), rl_key, ptr);
    }
    }
}

int main(void)
{
    pthread_t t[5];
    int i;
    for(i = 0; i < 5; i++) {
        pthread_create(&t[i], NULL, foo, NULL);
    }


    for(i = 0; i < 5; i++) {
        pthread_join(t[i], NULL);
    }

    return 0;
}
pthread_once(&rl_once, create_key)
不管多少個線程只有第一個執行它的線程運行一次被掉函數,保證了分配的rl_key的安全。

if ( (ptr = pthread_getspecific(rl_key)) == NULL) {// 檢查當前線程key域是否有值

檢查當前線程的pthread結構的key域是否有值

pthread_setspecific(rl_key, ptr);// 設置key的值
設置當前線程的pthread結構的key域的值

void destructor(void *value) {
    printf("pthread:%d destructor value(%p)\n", pthread_self(), value);
    free(value);// 線程結束後執行
}
線程結束後,會自動掉此析構函數,釋放分配資源。

上例運行結果如下:

pthread:1691465472 create_key 0
pthread:1691465472 key(0) value(0x7f2b5c0008c0)
pthread:1680975616 key(0) value(0x7f2b540008c0)
pthread:1659995904 key(0) value(0x7f2b440008c0)
pthread:1659995904 destructor value(0x7f2b440008c0)
pthread:1680975616 destructor value(0x7f2b540008c0)
pthread:1691465472 destructor value(0x7f2b5c0008c0)
pthread:1649506048 key(0) value(0x7f2b3c0008c0)
pthread:1649506048 destructor value(0x7f2b3c0008c0)
pthread:1670485760 key(0) value(0x7f2b4c0008c0)
pthread:1670485760 destructor value(0x7f2b4c0008c0)


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