同步

條件變數

互斥鎖回答的是「我可以碰這份資料嗎?」條件變數回答的是另一個問題:「我持著鎖,但資料還沒到我需要的狀態——我該怎麼睡到有人把它準備好為止,而不在等待時霸著鎖?」想像鎖室旁有一間安靜的候診室:一個發現世界還沒準備好的執行緒,走進候診室、放開鎖、小睡一下,直到有人拍它的肩膀。

條件變數總是與一把互斥鎖搭配運作。它的核心操作是 wait:持著互斥鎖時,你呼叫 pthread_cond_wait(&cv, &m),它會原子地釋放互斥鎖並讓執行緒睡去,這整件事是一個不可分割的步驟(所以它不會漏掉發生在縫隙裡的喚醒)。當執行緒稍後被喚醒,wait 會在回傳前重新取得互斥鎖,於是你又回到了臨界區間裡。另一個執行緒改變共享狀態後,呼叫 signal 喚醒一個等待者,或 broadcast 喚醒全部。不可妥協的樣式是:在一個重新檢查述語的迴圈裡 wait——while (!ready) pthread_cond_wait(&cv, &m); ——絕不是單純的 if。醒來後你必須重新測試條件,因為等你重新取得鎖時狀態可能又變了,也因為有偽喚醒。

條件變數是執行緒「等待事件」(而非等待鎖)的方式:一個變空的佇列、一個變滿的緩衝區、一份完成的工作。它是生產者消費者樣式、以及任何「等到某條件變真」情境的標準構件。經典錯誤是在不持鎖、或還沒先更新狀態的情況下發出 signal,於是某個等待者檢查述語、發現為假、正好在 signal 觸發的那一瞬間睡去——一次遺失的喚醒,從此永遠卡住。

消費者:lock(m); while (count == 0) cond_wait(&notEmpty, &m); take_item(); unlock(m);。生產者:lock(m); put_item(); cond_signal(&notEmpty); unlock(m);。用 while 迴圈(而非 if)正是它正確的關鍵。

wait 原子地放開鎖並睡去;醒來後永遠在 while 迴圈裡重新檢查述語。

條件變數不保存任何狀態、也不記得任何 signal:在沒人等待時送出的 signal 就這樣丟了。這正是為什麼你要先在鎖內更新共享述語,也是為什麼等待者要對述語做迴圈、而非信任那個 signal。

又稱
condvarwait/signal variablecondition等待/通知變數