條件變數(condition variable)詳解
阿新 • • 發佈:2019-01-23
原理:
假設我們需要解決這樣一個問題:一個列表記錄需要處理的任務。一個執行緒往此列表新增任務,一個執行緒processTask處理此列表中的任務。這個問題的一個關鍵點在於processTask怎麼判斷任務列表不為空。一般有兩種方法: **一. processTask執行緒不斷查詢任務列表是否為空。** **二. 當列表不為空的時候,通知processTask處理相關任務。** 第一種方法往往是在一個while迴圈中判斷列表是否為空,如果為空則睡眠一段時間,如果不為空那麼把任務取出來並加以處理。此方案需要一個睡眠時間的平衡點如果睡眠時間太長,任務得不到及時的處理,降低效率。如果睡眠時間過短佔用CPU資源,卻什麼都不做,浪費CPU做其它事情的時間。 第二種方法就比較靠譜了,只有當列表不為空的時候才佔用CPU的時間,其它時間什麼都不做除了睡覺(執行緒掛起)。此方案就是我們所說的條件變數(condition variable)。 一般條件變數(condition variable)和互斥量結合使用。條件變數(condition variable)用途執行緒間資源的同步,互斥量(mutex)用途資源的互斥(唯一訪問)。 一個通俗易懂的例子: 你上廁所的時候,條件變數告訴你廁所是否為空位,有空位你上,沒空位你看著別人上。有空位的時候你不可能跟別人一起吧,所以你得給衛生間上鎖也就是互斥量了。 例:
bool bathroomEmpty = false;
pthread_t tid;
pthread_cond_t cond;
pthread_mutex_t mutex;
void* takeAleak(void *) {
pthread_mutex_lock(&mutex);
while (!bathroomEmpty)
pthread_cond_wait(&cond, &mutex);
// do whatever you want
pthread_mutex_unlock(&mutex);
}
void main() {
pthread_mutex_lock(&mutex);
// bathromm is empty now.
bathroomEmpty = true;
pthread_mutex_unlock(&mutex);
// tell somebody
pthread_cond_signal(&cond);
return 0;
}
有些人不禁會問為什麼要while (!bathroomEmpty), 而不用if (!bathroomEmpty)呢?那是因為當你醒過來後,發現廁所已經被佔了,那麼你還得繼續等。 pthread_cond_signal(&cond),可不可以放入pthread_mutex_unlock(&mutex)之前呢,答案是肯定的,只要保證通知別人的時候廁所已經空了。之前我們已經說了,你醒過來的時候廁所可能已經被佔了,那麼如何能保證你在醒來的時候廁所還是空的呢,那就把pthread_cond_signal(&cond)放在pthread_mutex_unlock(&mutex)之前吧。 可能你已經發現了,pthread_cond_wait()函式還需要將mutex變數傳入。是的,pthread_cond_wait()函式在等待之前會把互斥鎖開啟,以便其它執行緒向任務表裡面新增任務。在等到任務後再加上鎖。如果不把互斥鎖開啟,那麼執行緒將永遠等不到任務,加上了鎖,其它執行緒就沒法往任務列表裡面新增任務了,因為其它執行緒新增任務的時候需要擁有鎖。
broadcast(&cond), 與signal(&cond)的區別
顧名思義pthread_cond_broadcast喚醒所有的執行緒,pthread_cond_signal喚醒一個執行緒。你等待上廁所的同時也有其它人在等待,pthread_cond_signal通知你們其中的一人,pthread_cond_broad_cast通知你們所有人。當所有人被通知又只有一個空位的時候,你們所有人蜂擁而上,當然只能有一個人搶到空位,其它人繼續等。這就是為什麼要用while (!bathroomEmpty),而非if (!bathroomEmpty) 的原因了。當一個的人時候,其它人並不知道,所以不會發生通知所有人的競態條件。當只有一個空位的時候用pthread_cond_broad_cast,當有多個空位的時候用pthread_cond_broadcast。遺憾沒有通知具體人數的函式。