條件變數的虛假喚醒(spurious wakeups)問題
引言
條件變數是我們常用的同步原語之一,它的正確使用方式一般如下圖:
在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
相關文章
- 虛假喚醒
- 執行緒虛假喚醒問題剖析執行緒
- 每個鎖建立多個條件佇列以避免虛假喚醒佇列
- 多執行緒——虛假喚醒執行緒
- java多執行緒之消費生產模型-使用synchronized解決虛假喚醒Java執行緒模型synchronized
- 關於wake on lan遠端喚醒主機的問題,長時間關機無法遠端喚醒
- Condition條件變數變數
- pthread 條件變數thread變數
- 關於條件變數變數
- MySQL 變數和條件MySql變數
- [題解] 條件變數實現數字的交替輸出變數
- C++中的條件變數C++變數
- 使用channel代替條件變數變數
- Python執行緒專題7:條件變數Python執行緒變數
- linux 條件變數詳解Linux變數
- 互斥鎖和條件變數 (轉)變數
- sql 使用變數帶入in條件SQL變數
- .NET 網路喚醒
- sql 查詢條件問題SQL
- 多執行緒06:條件變數執行緒變數
- 條件變數如何避免丟失通知變數
- 動態條件的繫結變數的解決變數
- android 喚醒螢幕Android
- 語音喚醒實現
- App相互喚醒的幾種方式APP
- 關於 groutine 喚醒中 skipframes 引數不理解
- 宣告變數的問題變數
- win10睡眠模式怎麼喚醒_win10睡眠按啥鍵喚醒Win10模式
- win10如何關閉滑鼠喚醒_win10關閉滑鼠喚醒方法Win10
- 秒懂 Golang 中的 條件變數(sync.Cond)Golang變數
- 同步篇——事件等待與喚醒事件
- 遠端喚醒原始碼 (轉)原始碼
- 二、(LINUX 執行緒同步) 互斥量、條件變數以及生產者消費者問題Linux執行緒變數
- 變數可變性問題變數
- 64億封假郵件、120億美元損失 虛假電子郵件困擾全球
- 某條SQL突然變慢的問題分析SQL
- win10如何關閉自動喚醒_win10關閉自動喚醒方法Win10
- win10 休眠滑鼠喚醒怎麼設定_win10滑鼠休眠怎麼喚醒Win10