在前面兩篇文章中介紹了Linux下的互斥量和讀寫鎖兩種線程同步對象。這兩種線程同步對象都是用來保護特定資源(記憶體,檔案控制代碼等)的。假如某個線程需要等待系統處於某種狀態下才能繼續執行,Linux為瞭解決這種問題引入了條件變數這種線程同步對象,本文簡要介紹一下條件變數。
條件變數必須要與互斥量一起使用時,允許線程以無競爭的方式等待特定條件的發生。線程在等待條件變數和通知條件變數之前都必須要先把保護條件變數的互斥量加鎖。
和其他線程同步對象一樣,條件變數一樣需要初始化和銷毀,函數定義如下:
#include <pthread.h> int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
條件變數可以用PTHREAD_COND_INITIALIZER常量初始化,或者調用pthread_cond_init函數初始化。銷毀調用pthread_cond_destroy。
線程等待條件變數的函數定義如下:
#include <pthread.h> int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
兩個函數的差別在於前者指定一個逾時時間,在該時間內阻塞調用線程,並等待條件變數,如果規定時間內條件還沒有發生,則函數返回,並返回錯誤值ETIMEDOUT;而後者會一直阻塞調用線程,直到條件發生。
這個等待函數的使用有一些需要注意的地方,就是首先調用這兩個函數之前首先要對保護這個條件變數的互斥量加鎖,然後用這個加鎖的互斥量作為參數調用條件變
量等待函數。在等待函數內部將會對該互斥量解鎖,為什麼要對互斥量解鎖呢?前面提到條件發生時,通知條件變數這個動作也是在這個互斥量保護之下的,加入這
個互斥量不釋放,那麼它等待的條件永遠都不會發生了,將會進入死結狀態。馬上講到條件變數的通知函數。
典型條件變數的等待調用如下:1)對互斥量加鎖。2)用該互斥量做參數,調用等待條件變數的函數。3)條件發生之後,處理該條件。4)對該互斥量解鎖。
當某個條件變數已經滿足,可以調用如下函數來啟用等待線程。
#include <pthread.h> int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_signal將會啟用等待線程中的一個;pthread_cond_broadcast將會啟用所有的線程。另外請注意這兩個函數也需要互斥量來保護。
典型的條件變數啟用調用方法如下:1)對互斥量加鎖。2)修改條件,做自己該做的事兒。3)釋放互斥量。4)調用上面兩個函數通知等待線程。
上面四個步驟中請注意3和4,千萬不要搞反了,否則將會出現錯誤,等待函數可能會得不到執行(因為有線程競爭,後果未知)。具體原因請自行分析。
原文