APUE——線程私有數據

APUE鏈接
線程私有數據鏈接1
pthread_once鏈接

1. 線程私有數據

進程中的所有線程都可以訪問進程的整個地址空間。除了使用寄存器以外,線程沒有辦法阻止其他線程訪問它的數據,線程私有數據也不例外

線程私有數據(也稱線程特定數據)是存儲和查詢與某個線程相關的數據的一種機制。把這種數據稱爲線程私有數據或線程特定數據的原因是:希望每個線程可以獨立地訪問數據副本,而不需要擔心與其他線程的同步訪問問題。
errno被重新定義爲線程私有數據。這樣,一個線程做了設置errno的操作並不會影響進程中其他線程的errno的值。

在分配線程私有數據之前,需要創建與該數據關聯的鍵。這個鍵將用於獲取對線程私有數據的訪問權。使用pthread_key_create創建一個鍵。
destructor爲析構函數,爲線程正常退出的時候調用,比如pthread_exit,return,cancel,注意不能是exit等,或者是非正常退出,http://www.cnblogs.com/nufangrensheng/p/3509618.html
線程退出時,線程私有數據的析構函數將按照操作系統實現中定義的順序被調用。析構函數可能會調用另一個函數,該函數可能會創建新的線程私有數據而且把這個數據與當前的鍵關聯起來。當所有的析構函數都調用完成以後,系統會檢查是否還有非null的線程私有數據值與鍵關聯,如果有的話,再次調用析構函數。這個過程會一直重複直到線程所有的鍵都爲null值線程私有數據,或者已經做了PTHREAD_DESTRUCTOR_ITERATIONS(http://www.cnblogs.com/nufangrensheng/p/3522577.html中表12-1)中定義的最大次數的嘗試。

  1. 創建一個鍵 int pthread_key_create(pthread_key_t * key, void ( * destructor)(void*))
  2. 爲一個鍵設置線程私有數據 int pthread_setspecific(pthread_key_t key,const void *pointer));
  3. 從一個鍵讀取線程私有數據 void *pthread_getspecific(pthread_key_t key);
  4. 線程退出(退出時,會調用destructor釋放分配的緩存,參數是key所關聯的數據) void pthread_exit(void*value_ptr)
  5. 刪除一個鍵 int pthread_key_delete(pthread_key_t key),鍵佔用的內存被釋放。與該鍵關聯的線程數據所佔用的內存並不被釋放。因此,線程數據的釋放(destructor),必須在釋放鍵之前完成。
  6. 如果沒有線程私有數據值與鍵關聯,pthread_getspecific將返回一個空指針,可以據此來確定是否需要調用pthread_setspecific。

2. pthread_once

pthread_once保證該函數在進程中只執行一次

#include <pthread.h>
pthread_once_t initflag = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *initflag, void (*initfn)(void));
返回值:若成功則返回0,否則返回錯誤編號
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));

功能:本函數使用初值爲PTHREAD_ONCE_INIT的once_control變量保證init_routine()函數在本進程執行序列中僅執行一次。
在多線程編程環境下,儘管pthread_once()調用會出現在多個線程中,init_routine()函數僅執行一次,究竟在哪個線程中執行是不定的,是由內核調度來決定。

下列代碼保證pthread_key_create只執行一次,確保key值是相同的

void destructor(void *);

pthread_key_t key;
thread_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);
    ...
}

3. 例子分析

#include <limits.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>

static pthread_key_t key;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;

extern char **environ;

static void
pthread_init(void)
{
    pthread_key_create(&key, free);
}

char *
getenv(const char *name)
{
    int     i, len;
    char     *envbuf;

    pthread_once(&init_done, thread_init);
    pthread_mutex_lock(&env_mutex);
    envbuf = (char *)pthread_getspecific(key);
    if(envbuf == NULL)
    {
        envbuf = malloc(ARG_MAX);
        if(envbuf == NULL)
        {
            pthread_mutex_unlock(&env_mutex);
            return(NULL);
        }
        pthread_setspecific(key, envbuf);
    }
    len = strlen(name);
    for(i = 0; environ[i] != NULL; i++)
    {
        if((strncmp(name, environ[i], len) == 0) &&
           (environ[i][len] == '='))
        {
            strcpy(envbuf, &environ[i][len+1]);
            pthread_mutex_unlock(&env_mutex);
            return(envbuf);
        }
    }    
    pthread_mutex_unlock(&env_mutex);
    return(NULL);
}
/*三個線程:主線程,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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章