1. 程式人生 > >執行緒特定資料(筆記)

執行緒特定資料(筆記)

概述:

    執行緒特定資料,也稱為執行緒私有資料,是儲存和查詢某個特定資料相關資料的一種機制。

    在單執行緒程式中,我們經常要用到“全域性變數”以實現多個函式間共享資料。

    在多執行緒環境下,由於資料空間是共享的,因此全域性變數也為所有所有執行緒所共有。

    但有時應用程式設計中有必要提供執行緒私有的全域性變數,僅在某個執行緒中有效,但卻可以跨多個函式訪問。

    POSIX執行緒庫通過維護一定的資料結構來解決這個問題,這些資料被稱為執行緒特定資料(Thread-specific Data,或TSD)。    

相關函式:

    在分配執行緒特定資料之前,需要建立與該資料關聯的鍵。這個鍵將用於獲取對執行緒特定資料的訪問。使用pthread_key_create函式建立一個鍵。

  int pthread_key_create(pthread_key_t *key, void (*destr_function) (void*));

    建立的鍵儲存在pkey指向的記憶體單元中,這個鍵可以被程序中的所有執行緒使用,但每個執行緒與不同的執行緒特定資料地址相關聯。建立新鍵時,每個執行緒的資料地址設為空值。

    除了建立鍵以外,pthread_key_create可以為該鍵關聯一個可選擇的解構函式。當這個執行緒退出時,如果資料地址已經被置為非空值,那麼解構函式就會被呼叫,它唯一的引數就是該資料地址。如果傳入的解構函式為空,就表明沒有解構函式與這個鍵關聯。

    執行緒通常使用malloc為執行緒特定資料分配記憶體。解構函式通常釋放已分配的記憶體。

    對所有的執行緒,我們都可以通過呼叫pthread_key_delete函式來取消鍵與執行緒特定資料值之間的聯絡。

   int pthread_key_delete(pthread_key_t key);

    有些執行緒可能看到一個鍵值,而其他的執行緒看到的可能是另一個不同的鍵值,這取決於系統是如何排程執行緒的,解決這種競爭的辦法是使用pthread_once函式 。

       pthread_once_t once_control = PTHREAD_ONCE_INIT;

       int  pthread_once(pthread_once_t  *once_control,  void  (*init_routine)
       (void));

    once_control必須是一個非本地變數(如全域性變數或靜態變數),而且必須初始化為PTHREAD_ONCE_INIT。如果每個執行緒都呼叫pthread_once,系統就能保證初始化once_control只被呼叫一次,即系統首次呼叫pthread_once時。

    鍵一旦建立以後,就可以通過呼叫pthread_setspecific函式把鍵和執行緒特定資料關聯起來。可以通過pthread_getspecific獲得執行緒特定資料的地址。  

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

       void * pthread_getspecific(pthread_key_t key);

    如果沒有執行緒特定資料值與鍵關聯,pthread_getspecific將返回一個空指標,我們可以用這個空指標確定是否需要呼叫pthread_setspecific。

注:參考UNIX高階環境程式設計(第三版)

測試程式碼:

#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define ERR_EXIT(m)			\
	do				\
	{				\
		perror(m);		\
		exit(EXIT_FAILURE);     \
	}while(0);			\

//自定義執行緒資料
typedef struct tid
{
	pthread_t tid;
	char *str;
}tsd_t;

pthread_key_t key_tsd;
pthread_once_t once_control = PTHREAD_ONCE_INIT;


void destory_routine(void *value)
{
	printf("destory...\n");
	free(value);

}

void once_routine(void)
{
	pthread_key_create(&key_tsd, destory_routine);
	printf("key init...\n");
	
}

void *thread_routine(void *arg)
{
	pthread_once(&once_control,once_routine) ;
	tsd_t *value = (tsd_t*)malloc(sizeof(tsd_t));
	value->tid = pthread_self();
	value->str = (char*)arg;
	//設定執行緒特定資料
	pthread_setspecific(key_tsd, value);
	printf("%s setspecific %p\n",(char*)arg, value);

   	value = (tsd_t*)pthread_getspecific(key_tsd);
	printf("tid=0x%x str=%s\n",(int)value->tid, value->str);
	sleep(2);
   	value = (tsd_t*)pthread_getspecific(key_tsd);
	printf("tid=0x%x str=%s\n",(int)value->tid, value->str);
	sleep(2);
	return NULL;
}

int main(void)
{
	//pthread_key_create(&key_tsd, destory_routine);

	pthread_t tid1;
	pthread_t tid2;
	pthread_create(&tid1, NULL, thread_routine, "thread1");
	pthread_create(&tid2, NULL, thread_routine, "thread2");

	pthread_join(tid1, NULL);
	pthread_join(tid1, NULL);
	return 0;
}

執行截圖: