條件變量通過允許線程阻塞和等待另一個線程發送信號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。使用時,條件變量被用來阻塞一個線程,當條件不滿足時,線程往往解開相應的互斥鎖並等待條件發生變化。一旦其它的某個線程改變了條件變量,它將通知相應的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖並重新測試條件是否滿足。一般說來,條件變量被用來進行線承間的同步。
1.條件變量的結構為pthread_cond_t (相當於windows中的事件的作用)
2.條件變量的初始化
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,即此條件變量被同一進程內的各個線程使用。注意初始化條件變量只有未被使用時才能重新初始化或被釋放。
3.條件變量的釋放
釋放一個條件變量的函數為pthread_cond_ destroy(pthread_cond_t cond)
4.條件變量的等待
(1)函數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語句實現。
(2)另一個用來阻塞線程的函數是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段時間後,即使條件變量不滿足,阻塞也被解除。
5.條件變量的解除改變
函數pthread_cond_signal()的原型為:
extern int pthread_cond_signal_P ((pthread_cond_t *__cond));
它用來釋放被阻塞在條件變量cond上的一個線程。多個線程阻塞在此條件變量上時,哪一個線程被喚醒是由線程的調度策略 所決定的。要注意的是,必須用保護條件變量的互斥鎖來保護這個函數,否則條件滿足信號又可能在測試條件和調用pthread_cond_wait函數之間被發出,從而造成無限制的等待。
6.下面是使用函數pthread_cond_wait()和函數pthread_cond_signal()的一個簡單的例子。
pthread_mutex_t count_lock; pthread_cond_t count_nonzero; unsigned count; decrement_count () { pthread_mutex_lock (&count_lock); while(count==0) pthread_cond_wait( &count_nonzero, &count_lock); count=count -1; pthread_mutex_unlock (&count_lock); } increment_count(){ pthread_mutex_lock(&count_lock); if(count==0) pthread_cond_signal(&count_nonzero); count=count+1; pthread_mutex_unlock(&count_lock); }
count值為0時,decrement函數在pthread_cond_wait處被阻塞,並打開互斥鎖count_lock。此時,當調用到函數increment_count時,pthread_cond_signal()函數改變條件變量,告知decrement_count()停止阻塞。
有的時候僅僅依靠鎖住共享資源來使用它是不夠的。有時候共享資源只有某些狀態的時候才能夠使用。比方說,某個線程如果要從堆棧中讀取數據,那麼如果棧中沒有數據就必須等待數據被壓棧。這種情況下的同步使用互斥鎖
是不夠的。另一種同步的方式--條件變量,就可以使用在這種情況下。
條件變量的使用總是和互斥鎖及共享資源聯系在一起的。線程首先鎖住互斥鎖,然後檢驗共享資源的狀態是否處於可使用的狀態。如果不是,那麼線程就要等待條件變量。要指向這樣的操作就必須在等待的時候將互斥鎖解鎖,以
便其他線程可以訪問共享資源並改變其狀態。它還得保證從等到得線程返回時互斥體是被上鎖得。當另一個線程改變了共享資源的狀態時,它就要通知正在等待條件變量的線程,使之重新變回被互斥鎖阻塞的線程。
請看下面的例子:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> pthread_mutex_t cond_mutex=PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond_var=PTHREAD_COND_INITIALIZER; void* function_cond1(); void* function_cond2(); int count=0; #define COUNT_DONE 10 #define COUNT_HALT1 3 #define COUNT_HALT2 6 int main() { pthread_t thread1,thread2; pthread_create(&thread1,NULL,function_cond1,NULL); pthread_create(&thread2,NULL,function_cond2,NULL); pthread_join(thread1,NULL); pthread_join(thread2,NULL); printf("Final count: %d\n",count); return 0; } void* function_cond1() { for(;;) { pthread_mutex_lock(&cond_mutex); pthread_cond_wait(&cond_var,&cond_mutex); count++; printf("Counter value functionCount1: %d\n",count); pthread_mutex_unlock(&cond_mutex); if(count>=COUNT_DONE) return ; } } void* function_cond2() { for(;;) { pthread_mutex_lock(&cond_mutex); if(count<COUNT_HALT1||count>COUNT_HALT2) { // Condition of if statement has been met. // Signal to free waiting thread by freeing the mutex. // Note: functionCount1() is now permitted to modify "count". pthread_cond_signal(&cond_var); } else { count++; printf("Counter value functionCount2: %d\n",count); } pthread_mutex_unlock(&cond_mutex); if(count>=COUNT_DONE) return ; } }
結果:
兩個線程同時運行,當在count<COUNT_HALT1||count>COUNT_HALT2的時候,線程1是一直等待的。只有線程2條件到達的時候,調用pthread_cond_signal(&cond_var);通知
線程1,pthread_cond_wait(&cond_var,&cond_mutex);