Posix線程編程指南(2)

這是一個關於Posix線程編程的專欄。做者在闡明概念的基礎上,將向您詳細講述Posix線程庫API。本文是第2篇將向您講述 線程的建立與取消
一.概念及做用
在單線程程序中,咱們常常要用到"全局變量"以實現多個函數間共享數據。在多線程環境下,因爲數據空間是共享的,所以全局變量也爲全部線程所共有。但有時應用程序設計中有必要提供線程私有的全局變量,僅在某個線程中有效,但卻能夠跨多個函數訪問,好比程序可能須要每一個線程維護一個鏈表,而使用相同的函數操做,最簡單的辦法就是使用同名而不一樣變量地址的線程相關數據結構。這樣的數據結構能夠由Posix線程庫維護,稱爲線程私有數據(Thread-specific Data,或TSD)。
二.建立和註銷
Posix定義了兩個API分別用來建立和註銷TSD:
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *))
該函數從TSD池中分配一項,將其值賦給key供之後訪問使用。若是destr_function不爲空,在線程退出(pthread_exit())時將以key所關聯的數據爲參數調用destr_function(),以釋放分配的緩衝區。
不論哪一個線程調用pthread_key_create(),所建立的key都是全部線程可訪問的,但各個線程可根據本身的須要往key中填入不一樣的值,這就至關於提供了一個同名而不一樣值的全局變量。在LinuxThreads的實現中,TSD池用一個結構數組表示:
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = { { 0, NULL } };
建立一個TSD就至關於將結構數組中的某一項設置爲"in_use",並將其索引返回給*key,而後設置destructor函數爲destr_function。
註銷一個TSD採用以下API:
int pthread_key_delete(pthread_key_t key)
這個函數並不檢查當前是否有線程正使用該TSD,也不會調用清理函數(destr_function),而只是將TSD釋放以供下一次調用pthread_key_create()使用。在LinuxThreads中,它還會將與之相關的線程數據項設爲NULL(見"訪問")。
三.訪問
TSD的讀寫都經過專門的Posix Thread函數進行,其API定義以下:
int pthread_setspecific(pthread_key_t key, const void *pointer)
void * pthread_getspecific(pthread_key_t key)
寫入(pthread_setspecific())時,將pointer的值(不是所指的內容)與key相關聯,而相應的讀出函數則將與key相關聯的數據讀出來。數據類型都設爲void *,所以能夠指向任何類型的數據。
在LinuxThreads中,使用了一個位於線程描述結構(_pthread_descr_struct)中的二維void *指針數組來存放與key關聯的數據,數組大小由如下幾個宏來講明:
#define PTHREAD_KEY_2NDLEVEL_SIZE 32
#define PTHREAD_KEY_1STLEVEL_SIZE ((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1)/PTHREAD_KEY_2NDLEVEL_SIZE)
其中在/usr/include/bits/local_lim.h中定義了PTHREAD_KEYS_MAX爲1024,所以一維數組大小爲32。
而具體存放的位置由key值通過如下計算獲得:
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZEidx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE
也就是說,數據存放與一個32×32的稀疏矩陣中。一樣,訪問的時候也由key值通過相似計算獲得數據所在位置索引,再取出其中內容返回。
四.使用範例
如下這個例子沒有什麼實際意義,只是說明如何使用,以及可以使用這一機制達到存儲線程私有數據的目的。
      
#i nclude <stdio.h>
#i nclude <pthread.h>
pthread_key_t key;
void echomsg(int t){
printf("destructor excuted in thread %d,param=%dn",pthread_self(),t);
}
void * child1(void *arg){
int tid=pthread_self();
printf("thread %d entern",tid);
pthread_setspecific(key,(void *)tid);
sleep(2);
printf("thread %d returns %dn",tid,pthread_getspecific(key));
sleep(5);
}
void * child2(void *arg){
int tid=pthread_self();
printf("thread %d entern",tid);
pthread_setspecific(key,(void *)tid);
sleep(1);
printf("thread %d returns %dn",tid,pthread_getspecific(key));
sleep(5);
}
int main(void){
int tid1,tid2;
printf("hellon");
pthread_key_create(&key,echomsg);
pthread_create(&tid1,NULL,child1,NULL);
pthread_create(&tid2,NULL,child2,NULL);
sleep(10);
pthread_key_delete(key);
printf("main thread exitn");
return 0;
}
#i nclude <stdio.h>
#i nclude <pthread.h>
pthread_key_t key;
void echomsg(int t){
printf("destructor excuted in thread %d,param=%dn",pthread_self(),t);
}
void * child1(void *arg){
int tid=pthread_self();
printf("thread %d entern",tid);
pthread_setspecific(key,(void *)tid);
sleep(2);
printf("thread %d returns %dn",tid,pthread_getspecific(key));
sleep(5);
}
void * child2(void *arg){
int tid=pthread_self();
printf("thread %d entern",tid);
pthread_setspecific(key,(void *)tid);
sleep(1);
printf("thread %d returns %dn",tid,pthread_getspecific(key));
sleep(5);
}
int main(void){
int tid1,tid2;
printf("hellon");
pthread_key_create(&key,echomsg);
pthread_create(&tid1,NULL,child1,NULL);
pthread_create(&tid2,NULL,child2,NULL);
sleep(10);
pthread_key_delete(key);
printf("main thread exitn");
return 0;
}
給例程建立兩個線程分別設置同一個線程私有數據爲本身的線程ID,爲了檢驗其私有性,程序錯開了兩個線程私有數據的寫入和讀出的時間,從程序運行結果能夠看出,兩個線程對TSD的修改互不干擾。同時,當線程退出時,清理函數會自動執行,參數爲tid。

0javascript

收藏html

xunet

33篇文章,12W+人氣,0粉絲

Ctrl+Enter 發佈java

發佈git

取消ajax

推薦專欄更多

微服務技術架構和大數據治理實戰

大數據時代的微服務之路

共18章 | 純潔微笑

¥51.00 710人訂閱
基於Python的DevOps實戰

自動化運維開發新概念

共20章 | 撫琴煮酒

¥51.00 569人訂閱

掃一掃,領取大禮包

0

分享
xunet
相關文章
相關標籤/搜索