接下來,給出 pthread_cond_wait 和 pthread_cond_signal 的偽**(參考man pthread_cond_signal)。語句後面的編號代表時間上的執行順序。
pthread_cond_wait(mutex, cond)
else
pthread_mutex_lock(mutex); /* 13 */
}pthread_cond_signal(cond)
pthread_mutex_unlock(cond->mutex); /* 9 */
}
對於一般情況,wait 完成進入等待佇列的情況比較簡單,就不考慮了。我們考慮在釋放互斥鎖後,進入 wait 等待佇列前的情況:
假設只有兩個執行緒 a 和 b
執行緒 a 執行完語句 2 後正在嘗試進入等待佇列(已經解鎖但是尚未阻塞),即期望執行語句 10 以及後面的進入等待佇列. 同時執行緒 b 正在執行 pthread_cond_signal 中的語句 3 到語句 9. 此時等待隊列為空,所以 if (cond->waiter) 條件不成立。
另外條件也發生了改變(cond->value++
),執行緒 b 完成了 pthread_signal 返回後,執行緒 a 執行語句 10,因為條件已經成立,所以執行緒 a 不會進入等待佇列直接返回。巨集觀上看起來,好像就是從「等待佇列」中被喚醒一樣。
pthread_cond_wait 被分解成了三步,其中 a1 和 a2 是一次執行完的。這兩個步驟是原子的。而實際上,pthread_cond_wait 的實現應該像本文上面的偽**,即阻塞不是必須的,能給人一種錯覺就夠了。
再次回顧一下前文所述:
如果執行緒 a 在釋放鎖後(語句 a1),執行語句 a2 的即將要阻塞的時候,執行緒 b 此時呼叫 pthread_cond_signal 或者 pthread_cond_broadcast ,感覺就好像即將要被阻塞的執行緒 a 已經阻塞過一樣。現在你應該理解了所謂的「原子」是怎麼做的了吧。
英文術語叫 spurious wakeup。
在前一篇文章中說,pthread_cond_signal 可以喚醒佇列中的乙個執行緒,而實際上,它也可能喚醒不止乙個執行緒。
man 手冊做此解釋::
在多核系統中,要避免 pthread_cond_signal 喚醒超過乙個以上的執行緒,似乎是不可能的。
繼續使用前面使用的偽**。
假設只有三個執行緒 a 、b 和 c
執行緒 a 執行完語句 2 後正在嘗試進入等待佇列(已經解鎖但是尚未阻塞),即期望執行語句 10 以及後面的進入等待佇列. 同時執行緒 b 正在執行 pthread_cond_signal 中的語句 3 到語句 9. 另一方面,執行緒 c 正處於等待佇列中。
和第 1 節中不一樣的是,語句 5if(cond->waiter)
成立,因為佇列中有執行緒 c。因此 pthread_cond_signal 會喚醒執行緒 c。而執行緒 a 也會因為if(value == cond->value)
直接執行語句13. 記住,此時的 a 是喚醒狀態,不是位於等待佇列中。
一旦執行緒 c 釋放鎖後,執行緒 a 就返回,然而此時,條件可能已經不成立(比如條件被程 c 更改),故出現虛假喚醒的狀態。
pthread_mutex_lock(&lock);
while(finished == 0)
pthread_mutex_unlock(&lock);
切記,這裡不要使用if
,而是while
!!! 目的在於防止虛假喚醒——spurious wakeup. 條件變數之虛假喚醒
如有錯誤請及時指正!3.如何避免虛假喚醒 當我們使用互斥量 mutex 與條件變數 condition variable 進行多執行緒同步時有可能會產生虛假喚醒現象,那麼究竟什麼是虛假喚醒,它會在什麼情況下被觸發,什麼情況下無需考慮虛假喚醒,如何避免?linux幫助中有提到 在多核處理器下,pthr...
深入理解條件變數 虛假喚醒
深入條件變數 pthread cond wait 和pthread cond signal 的偽實現 pthread cond wait mutex,cond 競爭失敗,那麼就不加入等待佇列了,相當於直接 喚醒 else pthread mutex unlock cond mutex 重新鎖住傳入的...
條件變數 虛假喚醒 放到while迴圈的原因
linux中幫助中提到 在多核處理器下,pthread cond signal可能會啟用多於乙個執行緒 阻塞在條件變數上的執行緒 結果是,當乙個執行緒呼叫pthread cond signal 後,多個呼叫pthread cond wait 或pthread cond timedwait 的執行緒返...