互斥鎖是用來給資源上鎖的,而條件變量是用來等待而不是用來上鎖的。
條件變量用來自動阻塞一個線程,直到某特殊情況發生為止。
通常條件變量和互斥鎖同時使用。
和條件變量使用有關的幾個重要函數:
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr); int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex); int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime); int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有線程的阻塞
1. 初始化:
條件變量采用的數據類型是pthread_cond_t, 在使用之前必須要進行初始化, 這包括兩種方式:
- 靜態: 可以把常量PTHREAD_COND_INITIALIZER給靜態分配的條件變量.
- 動態: pthread_cond_init函數, 是釋放動態條件變量的內存空間之前, 要用pthread_cond_destroy對其進行清理.
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
成功則返回0, 出錯則返回錯誤編號.
當pthread_cond_init的attr參數為NULL時, 會創建一個默認屬性的條件變量; 非默認情況以后討論.
2. 等待條件:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);
成功則返回0, 出錯則返回錯誤編號.
這兩個函數分別是阻塞等待和超時等待.
等待條件函數等待條件變為真, 傳遞給pthread_cond_wait的互斥量對條件進行保護, 調用者把鎖住的互斥量傳遞給函數. 函數把調用線程放到等待條件的線程列表上, 然后對互斥量解鎖, 這兩個操作是原子的. 這樣便關閉了條件檢查和線程進入休眠狀態等待條件改變這兩個操作之間的時間通道, 這樣線程就不會錯過條件的任何變化.
當pthread_cond_wait返回時, 互斥量再次被鎖住.
3. 通知條件:
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
成功則返回0, 出錯則返回錯誤編號.
這兩個函數用於通知線程條件已經滿足. 調用這兩個函數, 也稱向線程或條件發送信號. 必須注意, 一定要在改變條件狀態以后再給線程發送信號.
條件變量使用示例
下面是個非常簡單的例子,很好的演示了條件變量如何使用。
我將創建一個生產者線程,兩個消費者線程,通過互斥量和條件變量同步他們的生產和消費操作。
需要注意的一個地方是,pthread_cond_wait操作必須傳入一個條件變量和互斥量,線程先對互斥量上鎖,然后再執行pthread_cond_wait,pthread_cond_wait會將互斥量解鎖。
pthread_cond_wait在while循環中而不在if中的原因是,“可能被意外喚醒”,所以需要在喚醒之后再檢查緩沖區。
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <unistd.h> 4 5 pthread_cond_t condc,condp; 6 pthread_mutex_t the_mutex; 7 8 unsigned int buffer = 0; 9 const int MAX = 100; 10 11 void *producer(void *ptr){ 12 for(int i = 1; i < MAX; i++){ 13 pthread_mutex_lock(&the_mutex); 14 while(buffer != 0) pthread_cond_wait(&condp, &the_mutex); 15 sleep(1); 16 buffer = i; 17 printf("producer pthread produce one production %d.\n", i); 18 pthread_cond_broadcast(&condc);//喚醒兩個消費者線程 19 pthread_mutex_unlock(&the_mutex); 20 } 21 pthread_exit(0); 22 } 23 24 void *consumer1(void *ptr){ 25 for(int i = 1; i < MAX; i++){ 26 pthread_mutex_lock(&the_mutex); 27 while(buffer == 0) pthread_cond_wait(&condc, &the_mutex); 28 printf("consumer1 pthread consume one production %d.\n", buffer); 29 buffer = 0; 30 pthread_cond_signal(&condp); 31 pthread_mutex_unlock(&the_mutex); 32 } 33 pthread_exit(0); 34 } 35 void *consumer2(void *ptr){ 36 for(int i = 1; i < MAX; i++){ 37 pthread_mutex_lock(&the_mutex); 38 while(buffer == 0) pthread_cond_wait(&condc, &the_mutex); 39 printf("consumer2 pthread consume one production %d.\n", buffer); 40 buffer = 0; 41 pthread_cond_signal(&condp); 42 pthread_mutex_unlock(&the_mutex); 43 } 44 pthread_exit(0); 45 } 46 47 int main(void){ 48 pthread_t pro, con1, con2; 49 pthread_mutex_init(&the_mutex,0); 50 pthread_cond_init(&condc,0); 51 pthread_cond_init(&condp,0); 52 pthread_create(&con1, 0, consumer1, 0); 53 pthread_create(&pro, 0, producer, 0); 54 pthread_create(&con2, 0, consumer2, 0); 55 pthread_join(pro, 0); 56 pthread_join(con1, 0); 57 pthread_join(con2, 0); 58 pthread_cond_destroy(&condc); 59 pthread_cond_destroy(&condp); 60 pthread_mutex_destroy(&the_mutex); 61 return 0; 62 }
運行結果