多線程私有數據pthread_key_create


參照:http://blog.csdn.net/xiaohuangcat/article/details/18267561

 

在多線程的環境下,進程內的所有線程共享進程的數據空間。因此全局變量為所有線程共享。在程序設計中有時需要保存線程自己的全局變量,這種特殊的變量僅在線程內部有效。

如常見的errno,它返回標准的錯誤碼。errno不應該是一個局部變量。幾乎每個函數都應該可以訪問他,但他又不能作為是一個全局變量。否則在一個線程里輸出的很可能是另一個線程的

出錯信息,這個問題可以通過創建線程的私有數據(TSD  thread specific data)來解決。在線程內部,私有數據可以被各個函數訪問。但他對其他線程是屏蔽的。

線程私有數據采用了一鍵多值的技術,即一個鍵對應多個值。訪問數據時都是通過鍵值來訪問,好像是對一個變量進行訪問,其實是在訪問不同的數據。

 

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));第一個參數為指向一個鍵值的指針,第二個參數指明了一個destructor函數,

如果這個參數不為空,那么當每個線程結束時,系統將調用這個函數來釋放綁定在這個鍵上的內存塊。

key一旦被創建,所有線程都可以訪問它,但各線程可根據自己的需要往key中填入不同的值,這就相當於提供了一個同名而不同值的全局變量,一鍵多值。

一鍵多值靠的是一個關鍵數據結構數組即TSD池,創建一個TSD就相當於將結構數組中的某一項設置為“in_use”,並將其索引返回給*key,然后設置清理函數。

 

1、創建一個鍵

2、為一個鍵設置線程私有數據

3、從一個鍵讀取線程私有數據void *pthread_getspecific(pthread_key_t key);

4、線程退出(退出時,會調用destructor釋放分配的緩存,參數是key所關聯的數據)

5、刪除一個鍵

 

int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);

set是把一個變量的地址告訴key,一般放在變量定義之后,get會把這個地址讀出來,然后你自己轉義成相應的類型再去操作,注意變量的有效期。
只不過,在不同的線程里可以操作同一個key,他們不會沖突,比如線程a,b,c set同樣的key,分別get得到的地址會是之前各自傳進去的值。
這樣做的意義在於,可以寫一份線程代碼,通過key的方式多線程操作不同的數據。

int pthread_setspecific(pthread_key_t key, const void *value);該函數將value的值(不是內容)與key相關聯。用pthread_setspecific為一個鍵指定新的線程數據時,線程必須先釋放原有的線程數據用以回收空間。

 

nt pthread_key_delete(pthread_key_t key);用來刪除一個鍵,刪除后,鍵所占用的內存將被釋放。注銷一個TSD,這個函數並不檢查當前是否有線程正使用該TSD,也不會調用清理函數(destr_function),

而只是將TSD釋放以供下一次調用pthread_key_create()使用。需要注意的是,鍵占用的內存被釋放。與該鍵關聯的線程數據所占用的內存並不被釋放。因此,線程數據的釋放,必須在釋放鍵之前完成。

 

簡單的示例代碼:

#include <pthread.h>
#include <stdio.h>

pthread_key_t key;
pthread_t thid1;
pthread_t thid2;

void* thread2(void* arg)
{
    printf("thread:%lu is running\n", pthread_self());
    
    int key_va = 3 ;

    pthread_setspecific(key, (void*)key_va);
    
    printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));
}


void* thread1(void* arg)
{
    printf("thread:%lu is running\n", pthread_self());
    
    int key_va = 5;
    
    pthread_setspecific(key, (void*)key_va);

    pthread_create(&thid2, NULL, thread2, NULL);

    printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));
}


int main()
{
    printf("main thread:%lu is running\n", pthread_self());

    pthread_key_create(&key, NULL);

    pthread_create(&thid1, NULL, thread1, NULL);

    pthread_join(thid1, NULL);
    pthread_join(thid2, NULL);

    int key_va = 1;
    pthread_setspecific(key, (void*)key_va);
    
    printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));

    pthread_key_delete(key);
        
    printf("main thread exit\n");
    return 0;
}

 釋放空間、每次設置之前判斷的代碼:

/*三個線程:主線程,th1,th2各自有自己的私有數據區域
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>

static pthread_key_t str_key;
//define a static variable that only be allocated once
static pthread_once_t str_alloc_key_once=PTHREAD_ONCE_INIT;
static void str_alloc_key();
static void str_alloc_destroy_accu(void* accu);

char* str_accumulate(const char* s)
{    char* accu;
    
    pthread_once(&str_alloc_key_once,str_alloc_key);//str_alloc_key()這個函數只調用一次
    accu=(char*)pthread_getspecific(str_key);//取得該線程對應的關鍵字所關聯的私有數據空間首址
    if(accu==NULL)//每個新剛創建的線程這個值一定是NULL(沒有指向任何已分配的數據空間)
    {    accu=malloc(1024);//用上面取得的值指向新分配的空間
        if(accu==NULL)    return NULL;
        accu[0]=0;//為后面strcat()作准備
      
        pthread_setspecific(str_key,(void*)accu);//設置該線程對應的關鍵字關聯的私有數據空間
        printf("Thread %lx: allocating buffer at %p\n",pthread_self(),accu);
     }
     strcat(accu,s);
     return accu;
}
//設置私有數據空間的釋放內存函數
static void str_alloc_key()
{    pthread_key_create(&str_key,str_alloc_destroy_accu);/*創建關鍵字及其對應的內存釋放函數,當進程創建關鍵字后,這個關鍵字是NULL。之后每創建一個線程os都會分給一個對應的關鍵字,關鍵字關聯線程私有數據空間首址,初始化時是NULL*/
    printf("Thread %lx: allocated key %d\n",pthread_self(),str_key);
}
/*線程退出時釋放私有數據空間,注意主線程必須調用pthread_exit()(調用exit()不行)才能執行該函數釋放accu指向的空間*/
static void str_alloc_destroy_accu(void* accu)
{    printf("Thread %lx: freeing buffer at %p\n",pthread_self(),accu);
    free(accu);
}
//線程入口函數
void* process(void *arg)
{    char* res;
    res=str_accumulate("Resule of ");
    if(strcmp((char*)arg,"first")==0)
        sleep(3);
    res=str_accumulate((char*)arg);
    res=str_accumulate(" thread");
    printf("Thread %lx: \"%s\"\n",pthread_self(),res);
    return NULL;
}
//主線程函數
int main(int argc,char* argv[])
{    char* res;
    pthread_t th1,th2;
    res=str_accumulate("Result of ");
    pthread_create(&th1,NULL,process,(void*)"first");
    pthread_create(&th2,NULL,process,(void*)"second");
    res=str_accumulate("initial thread");
    printf("Thread %lx: \"%s\"\n",pthread_self(),res);
    pthread_join(th1,NULL);
    pthread_join(th2,NULL);
    pthread_exit(0);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM