00. 目錄
文章目錄
01. 線程之間共享數據
在多線程程序中,經常要用全局變量來實現多個函數間的數據共享。由於數據空間是共享的,因此全局變量也爲所有線程共有。
測試代碼:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
int key = 100; //全局變量
void *helloworld_one(void *arg)
{
printf("the message is %s\n",(char *)arg);
key = 10;
printf("key=%d, the child id is %lu\n", key, pthread_self());
return NULL;
}
void *helloworld_two(void *arg)
{
printf("the message is %s\n", (char *)arg);
sleep(1);
printf("key=%d, the child id is %lu\n", key, pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread_id_one;
pthread_t thread_id_two;
//創建線程
pthread_create(&thread_id_one, NULL, helloworld_one, "helloworld_one");
pthread_create(&thread_id_two, NULL, helloworld_two, "helloworld_two");
//等待線程結束,回收資源
pthread_join(thread_id_one, NULL);
pthread_join(thread_id_two, NULL);
return 0;
}
測試結果:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c -pthread
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out
the message is helloworld_one
key=10, the child id is 139954627110656
the message is helloworld_two
key=10, the child id is 139954618717952
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$
由運行結果可以看出,其中一個線程對全局變量的修改將影響到另一個線程的訪問。
02. 線程私有數據
但有時應用程序設計中必要提供線程私有的全局變量,這個變量僅在線程中有效,但卻可以跨過多個函數訪問。比如在程序裏可能需要每個線程維護一個鏈表,而會使用相同的函數來操作這個鏈表,最簡單的方法就是使用同名而不同變量地址的線程相關數據結構。這樣的數據結構可以由 Posix 線程庫維護,成爲線程私有數據 (Thread-specific Data,或稱爲 TSD)。
2.1 創建線程私有數據
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
功能:
創建一個類型爲 pthread_key_t 類型的私有數據變量( key )。
參數:
key:在分配( malloc )線程私有數據之前,需要創建和線程私有數據相關聯的鍵( key ),
這個鍵的功能是獲得對線程私有數據的訪問權。
destructor:清理函數名字( 如:fun )。當線程退出時,如果線程私有數據地址不是非 NULL,
此函數會自動被調用。該函數指針可以設成 NULL ,這樣系統將調用默認的清理函數。
回調函數其定義如下:
void fun(void *arg)
{
// arg 爲 key 值
}
返回值:
成功:0
失敗:非 0
不論哪個線程調用 pthread_key_create(),所創建的 key 都是所有線程可訪問,但各個線程可根據自己的需要往 key 中填入不同的值,相當於提供了一個同名不同值的變量。
2.2 銷燬線程私有數據
int pthread_key_delete(pthread_key_t key);
功能:
註銷線程私有數據。這個函數並不會檢查當前是否有線程正使用線程私有數據( key ),
也不會調用清理函數 destructor() ,而只是將線程私有數據( key )釋放以供下一次
調用pthread_key_create() 使用。
參數:
key:待註銷的私有數據。
返回值:
成功:0
失敗:非 0
2.3 關聯線程私有數據成員
int pthread_setspecific(pthread_key_t key, const void *value);
功能:
設置線程私有數據( key ) 和 value 關聯,注意,是 value 的值(不是所指的內容)和 key 相關聯。
參數:
key:線程私有數據。
value:和 key 相關聯的指針。
返回值:
成功:0
失敗:非 0
2.4 讀取線程私有數據所關聯的值
void *pthread_getspecific(pthread_key_t key);
功能:
讀取線程私有數據( key )所關聯的值。
參數:
key:線程私有數據。
返回值:
成功:線程私有數據( key )所關聯的值。
失敗:NULL
03. 案例實踐
測試程序
// this is the test code for pthread_key
#include <stdio.h>
#include <pthread.h>
pthread_key_t key; // 私有數據,全局變量
void echomsg(void *t)
{
printf("[destructor] thread_id = %lu, param = %p\n", pthread_self(), t);
}
void *child1(void *arg)
{
int i = 10;
pthread_t tid = pthread_self(); //線程號
printf("\nset key value %d in thread %lu\n", i, tid);
pthread_setspecific(key, &i); // 設置私有數據
printf("thread one sleep 2 until thread two finish\n\n");
sleep(2);
printf("\nthread %lu returns %d, add is %p\n",
tid, *((int *)pthread_getspecific(key)), pthread_getspecific(key) );
}
void *child2(void *arg)
{
int temp = 20;
pthread_t tid = pthread_self(); //線程號
printf("\nset key value %d in thread %lu\n", temp, tid);
pthread_setspecific(key, &temp); //設置私有數據
sleep(1);
printf("thread %lu returns %d, add is %p\n",
tid, *((int *)pthread_getspecific(key)), pthread_getspecific(key));
}
int main(void)
{
pthread_t tid1,tid2;
pthread_key_create(&key, echomsg); // 創建
pthread_create(&tid1, NULL, child1, NULL);
pthread_create(&tid2, NULL, child2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key); // 註銷
return 0;
}
執行結果:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out
set key value 10 in thread 140551366268672
set key value 20 in thread 140551357875968
thread one sleep 2 until thread two finish
thread 140551357875968 returns 20, add is 0x7fd4a9c23ecc
[destructor] thread_id = 140551357875968, param = 0x7fd4a9c23ecc
thread 140551366268672 returns 10, add is 0x7fd4aa424ecc
[destructor] thread_id = 140551366268672, param = 0x7fd4aa424ecc
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$
從運行結果來看,各線程對自己的私有數據操作互不影響。也就是說,雖然 key 是同名且全局,但訪問的內存空間並不是同一個。