2011-12-21 75 views
104

引述手冊頁:爲什麼pthread_cond_wait有虛假喚醒?

當使用條件變量始終存在涉及與每個條件等待這是真的,如果線程應進行相關的共享變量的布爾謂詞。可能會發生來自pthread_cond_timedwait()或pthread_cond_wait()函數的虛假喚醒。由於從pthread_cond_timedwait()或pthread_cond_wait()返回並不意味着這個謂詞的值,所以在返回時應該重新評估謂詞。

因此,即使您沒有發出信號,pthread_cond_wait也會返回。乍一看,這似乎很殘酷。它會像一個函數,它在實際達到正確的返回語句之前隨機返回錯誤的值或隨機返回。這似乎是一個主要的錯誤。但他們選擇在手冊頁中記錄而不是修復它的事實似乎表明,爲什麼pthread_cond_wait最終會虛假地醒來。據推測,它是如何工作的內在原因使得它無法得到幫助。問題是什麼。

爲什麼確實pthread_cond_wait虛假地返回?爲什麼它不能保證只有當信號正確時才醒來?任何人都可以解釋其虛假行爲的原因嗎?

+4

我可以想象它與處理捕獲信號時返回有關。大多數* nixes在信號中斷後不會重新啓動阻塞呼叫;他們只是設置/返回一個表示發生信號的錯誤代碼。 – cHao 2011-12-21 18:34:47

+0

@cHao:雖然請注意,因爲條件變量無論如何都有*其他*虛假喚醒的原因,處理一個信號對於'pthread_cond_(timed)wait'不是一個錯誤:「如果一個信號被傳遞...線程恢復等待條件變量,如同它沒有中斷一樣,或者由於虛假喚醒它將返回零「。其他的阻塞函數表示當被一個信號中斷(例如'read')或者需要恢復(例如'pthread_mutex_lock')時'EINTR'。因此,如果沒有其他原因導致虛假喚醒,可以將pthread_cond_wait定義爲其中任何一種。 – 2011-12-21 19:09:41

+3

維基百科上的一篇相關文章:[Spurious wakeup](http://en.wikipedia.org/wiki/Spurious_wakeup) – Palec 2015-01-07 22:51:49

回答

64

下面的解釋是由David R. Butenhof在"Programming with POSIX Threads"(第80頁)給出:

虛假喚醒可能聽起來很奇怪,但在一些多處理器系統,使得狀態喚醒完全可以預測可能會大幅放緩的所有條件可變操作。

在下面comp.programming.threads discussion,他在設計背後的思維拓展:

 
Patrick Doyle wrote: 
> In article , Tom Payne wrote: 
> >Kaz Kylheku wrote: 
> >: It is so because implementations can sometimes not avoid inserting 
> >: these spurious wakeups; it might be costly to prevent them. 

> >But why? Why is this so difficult? For example, are we talking about 
> >situations where a wait times out just as a signal arrives? 

> You know, I wonder if the designers of pthreads used logic like this: 
> users of condition variables have to check the condition on exit anyway, 
> so we will not be placing any additional burden on them if we allow 
> spurious wakeups; and since it is conceivable that allowing spurious 
> wakeups could make an implementation faster, it can only help if we 
> allow them. 

> They may not have had any particular implementation in mind. 

You're actually not far off at all, except you didn't push it far enough. 

The intent was to force correct/robust code by requiring predicate loops. This was 
driven by the provably correct academic contingent among the "core threadies" in 
the working group, though I don't think anyone really disagreed with the intent 
once they understood what it meant. 

We followed that intent with several levels of justification. The first was that 
"religiously" using a loop protects the application against its own imperfect 
coding practices. The second was that it wasn't difficult to abstractly imagine 
machines and implementation code that could exploit this requirement to improve 
the performance of average condition wait operations through optimizing the 
synchronization mechanisms. 
/------------------[ [email protected] ]------------------\ 
| Compaq Computer Corporation    POSIX Thread Architect | 
|  My book: http://www.awl.com/cseng/titles/0-201-63392-2/  | 
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/ 

75

至少有兩件事情「虛假喚醒」可能意味着:

  • 阻塞在一個線程即使沒有呼叫發送信號或廣播發生,pthread_cond_wait也可以從呼叫中返回。
  • pthread_cond_wait中的一個線程因爲調用信號或廣播而返回,但重新獲取互斥體後,發現底層謂詞不再成立。

但是即使條件變量實現不允許前一種情況,後一種情況也會發生。考慮一個生產者消費者隊列和三個線程。

  • 線程1剛剛出列了一個元素並釋放了互斥量,而隊列現在是空的。該線程正在處理它在某些CPU上獲取的元素。
  • 線程2嘗試使元素出列,但在互斥體下檢查時發現隊列爲空,調用pthread_cond_wait,並在呼叫等待信號/廣播中阻塞。
  • 線程3獲取互斥鎖,向隊列中插入新元素,通知條件變量並釋放鎖。
  • 作爲對來自線程3的通知的響應,等待條件的線程2計劃運行。
  • 但是,在線程2設法獲取CPU並獲取隊列鎖之前,線程1完成當前任務並返回隊列以進行更多工作。它獲得隊列鎖定,檢查謂詞,並發現隊列中有工作。它繼續出線插入線程3的項目,釋放鎖定,並執行線程3入隊的項目。
  • 線程2現在獲取一個CPU並獲取鎖,但是當它檢查謂詞時,它會發現隊列爲空。線程1'偷了'這個項目,所以喚醒似乎是虛假的。線程2需要再次等待條件。

因此,既然您已經總是需要檢查循環下的謂詞,如果底層條件變量可能有其他類型的虛假喚醒,則沒有區別。

+13

是的。 Essentialy,這是使用事件而不是使用計數的同步機制時發生的情況。可悲的是,看起來POSIX信號量(無論如何都是在Linux上)也受spurius喚醒的影響。我覺得有點奇怪,同步原語的基本功能失敗只是被認爲是'正常',並且必須在用戶級別解決:(假設系統調用被記錄在文檔中,開發人員應該是武裝的與「虛假segfault」部分,或者,也許是「虛假連接到錯誤的URL」或「錯誤的打開錯誤的文件」。 – 2011-12-22 11:07:00

+1

upvoted,很清楚。 – Alcott 2012-07-29 13:22:23

+2

「虛假喚醒」更常見的情況是最有可能的一方 - 對pthread_cond_broadcast()的調用的效果,假設你有5個線程池,兩個喚醒到廣播並完成工作,另外三個喚醒並且已經完成工作,多處理器系統也可以導致一個有條件的信號意外喚醒多個線程,代碼只是再次檢查謂詞,看到一個無效狀態,然後回到睡眠狀態,在任何一種情況下,檢查謂詞都可以解決問題。不應該使用原始的POSIX互斥體和條件。 – CubicleSoft 2016-05-23 09:40:02

6

pthread_cond_signal中的「條件信號的多重覺醒」部分具有涉及虛假wakekups的pthread_cond_wait和pthread_cond_signal的示例實現。

+1

我認爲這個答案是錯誤的,就它而言。該頁面上的示例實現具有「notify one」的實現,其等同於「notify all」;但它似乎並沒有產生實際的虛假*喚醒。線程喚醒的唯一方法是通過其他線程調用「notify all」,或者由其他線程調用標記爲「notify one」的其他線程 - 這實際上是「通知所有人」。 – Quuxplusone 2017-07-04 01:16:51

相關問題