2013-04-09 98 views
6

我的代碼中有一個與使用條件變量有關的死鎖問題。這比純粹的代碼問題更像是一個設計問題。一旦我理解了正確的設計,我就沒有問題了。我有以下情況:條件變量死鎖

  1. 線程A等待條件變量。
  2. 線程B調用notify_all,線程A喚醒。

這當然是我想要發生的,當一切按預期工作時會發生什麼。但有時候,我會得到以下場景:

  1. 線程A在它開始等待條件變量之前執行代碼。
  2. 線程B調用notify_all,認爲線程A正在等待。
  3. 線程A開始等待條件變量,沒有意識到線程B已經告訴它停止等待。僵局。

什麼是解決它的最好方法?我想不出一種可靠的方法來檢查線程A是否真的在等待,以便知道什麼時候應該在線程B中調用notify_all。我是否必須求助於timed_lock?我會討厭。

+0

你在使用什麼庫?什麼OS? – 2013-04-09 20:56:30

+0

使用信號量。 – 2013-04-09 21:01:07

回答

5

在線程A等待條件變量之前的一段時間內,它必須持有一個互斥鎖。最簡單的解決方案是確保線程B在調用notify_all時保持相同的互斥體。因此,像這樣:

std::mutex m; 
std::condition_variable cv; 
int the_condition = 0; 

Thread A: { 
    std::unique_lock<std::mutex> lock(m); 
    do something 
    while (the_condition == 0) { 
    cv.wait(lock); 
    } 
    now the_condition != 0 and thread A has the mutex 
    do something else 
} // releases the mutex; 

Thread B: { 
    std::unique_lock<std::mutex> lock(m); 
    do something that makes the_condition != 0 
    cv.notify_all(); 
} // releases the mutex 

這保證了線程B只做了notify_all()之前線程A獲取該互斥鎖或當線程A的條件變量的等待。

雖然這裏的另一個關鍵是等待the_condition的while循環成爲true。一旦A有互斥體,任何其他線程都不可能改變the_condition,直到A測試the_condition,發現它爲假,並開始等待(從而釋放互斥體)。

問題是:你真的在等待的是the_condition的值變成非零,std :: condition_variable :: notify_all只是告訴你線程B認爲線程A應該醒來並重新測試。

+3

+1請注意'std :: condition_variable :: wait'也有一個接受謂詞的重載。因此,而不是'while(the_condition == 0)cv.wait(lock);'你可以改爲寫'cv.wait(lock,[&] {return the_condition!= 0;});' – 2013-04-09 21:16:55

+0

@AndrewDurward: !我不知道。 – 2013-04-09 21:18:20

+1

啊,當然。可笑簡單。只要鎖定條件變量使用的同一個互斥量,在線程B中將條件更改爲實際爲真時,我已在您的代碼中實現了您的建議並解決了問題。謝謝! – 2013-04-09 21:59:20

2

條件變量必須始終與互斥,以避免由一個線程準備等待和之前所述第一線程上它導致死鎖實際上等待其可以用信號通知條件另一個線程創建的競爭條件相關聯。線程將永遠等待一個永遠不會發送的信號。任何互斥體都可以使用,互斥體和條件變量之間沒有明確的聯繫。