3
、條件變數
前一節中我們講述了如何使用互斥鎖來實現執行緒間資料的共享和通訊,互斥鎖乙個明顯的缺點是它只有兩種狀態:鎖定和非鎖定。而條件變數通過允許執行緒阻塞和等待另乙個執行緒傳送訊號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。使用時,條件變數被用來阻塞乙個執行緒,當條件不滿足時,執行緒往往解開相應的互斥鎖並等待條件發生變化。一旦其它的某個執行緒改變了條件變數,它將通知相應的條件變數喚醒乙個或多個正被此條件變數阻塞的執行緒。這些執行緒將重新鎖定互斥鎖並重新測試條件是否滿足。一般說來,條件變數被用來進行線承間的同步。
條件變數的結構為
pthread_cond_t
,函式pthread_cond_init
()被用來初始化乙個條件變數。它的原型為:
extern int pthread_cond_init __p ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr)); 其中
cond
是乙個指向結構
pthread_cond_t
的指標,
cond_attr
是乙個指向結構
pthread_condattr_t
的指標。結構
pthread_condattr_t
是條件變數的屬性結構,和互斥鎖一樣我們可以用它來設定條件變數是程序內可用還是程序間可用,預設值是
pthread_ process_private
,即此條件變數被同一程序內的各個執行緒使用。注意初始化條件變數只有未被使用時才能重新初始化或被釋放。釋放乙個條件變數的函式為
pthread_cond_ destroy
(pthread_cond_t cond
)。 函式
pthread_cond_wait
()使執行緒阻塞在乙個條件變數上。它的函式原型為:
extern int pthread_cond_wait __p ((pthread_cond_t *__cond,pthread_mutex_t *__mutex));
執行緒解開
mutex
指向的鎖並被條件變數
cond
阻塞。執行緒可以被函式
pthread_cond_signal
和函式pthread_cond_broadcast
喚醒,但是要注意的是,條件變數只是起阻塞和喚醒執行緒的作用,具體的判斷條件還需使用者給出,例如乙個變數是否為
0等等,這一點我們從後面的例子中可以看到。執行緒被喚醒後,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來執行緒應該仍阻塞在這裡,被等待被下一次喚醒。這個過程一般用
while
語句實現。
另乙個用來阻塞執行緒的函式是
pthread_cond_timedwait
(),它的原型為:
extern int pthread_cond_timedwait __p ((pthread_cond_t *__cond,pthread_mutex_t *__mutex, __const struct timespec *__abstime));
它比函式
pthread_cond_wait
()多了乙個時間引數,經歷
abstime
段時間後,即使條件變數不滿足,阻塞也被解除。
函式pthread_cond_signal
()的原型為:
extern int pthread_cond_signal __p ((pthread_cond_t *__cond));
它用來釋放被阻塞在條件變數
cond
上的乙個執行緒。多個執行緒阻塞在此條件變數上時,哪乙個執行緒被喚醒是由執行緒的排程策略所決定的。要注意的是,必須用保護條件變數的互斥鎖來保護這個函式,否則條件滿足訊號又可能在測試條件和呼叫
pthread_cond_wait
函式之間被發出,從而造成無限制的等待。下面是使用函式
pthread_cond_wait
()和函式
pthread_cond_signal
()的乙個簡單的例子。
count值為0
時,decrement
函式在pthread_cond_wait
處被阻塞,並開啟互斥鎖
count_lock
。此時,當呼叫到函式
increment_count
時,pthread_cond_signal
()函式改變條件變數,告知
decrement_count
()停止阻塞。讀者可以試著讓兩個執行緒分別執行這兩個函式,看看會出現什麼樣的結果。
函式pthread_cond_broadcast
(pthread_cond_t *cond
)用來喚醒所有被阻塞在條件變數
cond
上的執行緒。這些執行緒被喚醒後將再次競爭相應的互斥鎖,所以必須小心使用這個函式。
4、訊號量
訊號量本質上是乙個非負的整數計數器,它被用來控制對公共資源的訪問。當公共資源增加時,呼叫函式
sem_post
()增加訊號量。只有當訊號量值大於0時,才能使用公共資源,使用後,函式
sem_wait
()減少訊號量。函式
sem_trywait
()和函式
pthread_ mutex_trylock
()起同樣的作用,它是函式
sem_wait
()的非阻塞版本。下面我們逐個介紹和訊號量有關的一些函式,它們都在標頭檔案
/usr/include/semaphore.h
中定義。
訊號量的資料型別為結構
sem_t
,它本質上是乙個長整型的數。函式
sem_init
()用來初始化乙個訊號量。它的原型為:
extern int sem_init __p ((sem_t *__sem, int __pshared, unsigned int __value));
sem為指向訊號量結構的乙個指標;
pshared
不為0時此訊號量在程序間共享,否則只能為當前程序的所有執行緒共享;
value
給出了訊號量的初始值。
函式sem_post( sem_t *sem )
用來增加訊號量的值。當有執行緒阻塞在這個訊號量上時,呼叫這個函式會使其中的乙個執行緒不在阻塞,選擇機制同樣是由執行緒的排程策略決定的。
函式sem_wait( sem_t *sem )
被用來阻塞當前執行緒直到訊號量
sem的值大於
0,解除阻塞後將
sem的值減一,表明公共資源經使用後減少。函式
sem_trywait ( sem_t *sem )
是函式sem_wait
()的非阻塞版本,它直接將訊號量
sem的值減一。
函式sem_destroy(sem_t *sem)
用來釋放訊號量
sem。
下面我們來看乙個使用訊號量的例子。在這個例子中,一共有
4個執行緒,其中兩個執行緒負責從檔案讀取資料到公共的緩衝區,另兩個執行緒從緩衝區讀取資料作不同的處理(加和乘運算)。
在linux
下,我們用命令
gcc -lpthread sem.c -o sem
生成可執行檔案
sem。我們事先編輯好資料檔案
1.dat
和2.dat
,假設它們的內容分別為
1 2 3 4 5 6 7 8 9 10
和-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
,我們執行
sem,得到如下的結果:
multiply: -1*-2=2
plus: -1+-2=-3
multiply: 9*10=90
plus: -9+-10=-19
multiply: -7*-8=56
plus: -5+-6=-11
multiply: -3*-4=12
plus: 9+10=19
plus: 7+8=15
plus: 5+6=11
從中我們可以看出各個執行緒間的競爭關係。而數值並未按我們原先的順序顯示出來這是由於
size
這個數值被各個執行緒任意修改的緣故。這也往往是多執行緒程式設計要注意的問題。
小結
grep
比單執行緒的
grep
要快上幾倍,類似的例子還有很多。希望大家能用多執行緒技術寫出高效實用的好程式來。
Linux 多執行緒程式設計入門
建立執行緒 intpthread create pthread t restrict thread,const pthread attr t restrict attr,void start routine void void restrict arg 引數 thread 輸出執行緒id attr ...
Linux多執行緒程式設計入門 2
執行緒的分離狀態決定乙個執行緒以什麼樣的方式來終止自己。在上面的例子中,我們採用了執行緒的預設屬性,即為非分離狀態,這種情況下,原有的執行緒等待建立的執行緒結束。只有當 pthread join 函式返回時,建立的執行緒才算終止,才能釋放自己占用的系統資源。而分離執行緒不是這樣子的,它沒有被其他的執...
Linux 多執行緒程式設計入門 執行緒函式解釋
建立執行緒 intpthread create pthread t restrict thread,const pthread attr t restrict attr,void start routine void void restrict arg 引數 thread 輸出執行緒id attr ...