引言
條件變量是我們常用的同步原語之一,它的正確使用方式一般如下圖:
在wait端,我們必須把判斷布爾條件和wait()放到while循環中,而不能用if語句,原因是可能會引起虛假喚醒。
那麼,究竟什麼是虛假喚醒,導致虛假喚醒的原因又是什麼呢?
什麼是虛假喚醒?
舉個例子,我們現在有一個生產者-消費者隊列和三個線程。
1) 1號線程從隊列中獲取了一個元素,此時隊列變爲空。
2) 2號線程也想從隊列中獲取一個元素,但此時隊列爲空,2號線程便只能進入阻塞(cond.wait()),等待隊列非空。
3) 這時,3號線程將一個元素入隊,並調用cond.notify()喚醒條件變量。
4) 處於等待狀態的2號線程接收到3號線程的喚醒信號,便準備解除阻塞狀態,執行接下來的任務(獲取隊列中的元素)。
5) 然而可能出現這樣的情況:當2號線程準備獲得隊列的鎖,去獲取隊列中的元素時,此時1號線程剛好執行完之前的元素操作,返回再去請求隊列中的元素,1號線程便獲得隊列的鎖,檢查到隊列非空,就獲取到了3號線程剛剛入隊的元素,然後釋放隊列鎖。
6) 等到2號線程獲得隊列鎖,判斷髮現隊列仍爲空,1號線程“偷走了”這個元素,所以對於2號線程而言,這次喚醒就是“虛假”的,它需要再次等待隊列非空。
使用while()判斷的原因
在多核處理器下,pthread_cond_signal可能會激活多於一個線程(阻塞在條件變量上的線程)。結果就是,當一個線程調用pthread_cond_signal()後,多個調用pthread_cond_wait()或pthread_cond_timedwait()的線程返回。這種效應就稱爲“虛假喚醒”。
Linux man page中也有提到:
虛假喚醒造成的後果:
需要對條件進行再判斷以避免虛假喚醒:
如果用if判斷,多個等待線程在滿足if條件時都會被喚醒(虛假的),但實際上條件並不滿足,生產者生產出來的消費品已經被第一個線程消費了。
這就是我們使用while去做判斷而不是使用if的原因:因爲等待在條件變量上的線程被喚醒有可能不是因爲條件滿足而是由於虛假喚醒。所以,我們需要對條件變量的狀態進行不斷檢查直到其滿足條件,不僅要在pthread_cond_wait前檢查條件是否成立,在pthread_cond_wait之後也要檢查。
參考資料:
http://stackoverflow.com/questions/8594591/why-does-pthread-cond-wait-have-spurious-wakeups