2017-07-14 122 views
1

當我尋求在C++ 11中實現std::condition_variable的幫助時,我遇到了一個代碼here。在上面的問題中,可以正確執行此類代碼,但在函數void g()中添加註釋行會導致偶爾產生死鎖。我想知道爲什麼和std::condition_variable::wait()cpp reference真的讓我困惑)的內在機制。提前致謝。爲什麼重新使用條件變量的互斥會導致死鎖?

#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <iostream> 

std::mutex mtx; 
std::condition_variable cv; 

void f() { 
    { 
     std::unique_lock<std::mutex> lock(mtx); 
     cv.wait(lock); 
    } 
    std::cout << "f()\n"; 
} 

void g() { 
    // std::unique_lock<std::mutex> lock(mtx); adding this line will result in 
    //           deadlock. 
    std::this_thread::sleep_for(std::chrono::seconds(1)); 
    cv.notify_one(); 
} 

int main() { 
    for (int i = 1; i <= 100; i++) { 
     std::cout << i << std::endl; 
     std::thread t1{ f }; 
     std::thread t2{ g }; 
     t2.join(); 
     t1.join(); 
    } 
} 
+0

註釋行無法導致循環。也許你的意思是僵局? –

+0

是的。這是一個僵局。對不起,我英文很差。 :) –

回答

4

你應該與實際條件條件變量相關聯,也佔了虛假喚醒。在你的例子中,如果你先發送條件變量,然後通過wait()進入條件變量睡眠,那麼你的代碼會死鎖。

所以,你的代碼應該理想地看起來像以下(其中,如果你的信號你wait()睡覺前,變更後的條件將檢測你不應該睡覺)

void f() { 
    { 
     std::unique_lock<std::mutex> lock(mtx); 
     while (some_boolean) { 
      cv.wait(lock); 
     } 
    } 
    std::cout << "f()\n"; 
} 

void g() { 
    std::unique_lock<std::mutex> lock(mtx); 
    change_some_boolean(); 
    cv.notify_one(); 
} 

注意,它不當您致電notify_one()g()時,該鎖是否處於鎖定狀態。但是,您應該確保在change_some_boolean()之前持有鎖。

+0

這不能解決問題。如果'g()'在'f()'之前運行,那麼'f()'會掛起,因爲沒有人喚醒它。循環布爾值可避免**虛假喚醒**。它不會避免鎖定邏輯問題。 –

+0

@PeteBecker'f()'會掛起? while循環會檢測到'g()'在 – Curious

+0

之前運行@PeteBecker將條件與條件變量相關聯是在這種情況下同步的關鍵之一。 'f()'將如何掛起? – Curious

0

答案是在例如在您提供的鏈接:Condition Variable on cppreference

// Manual unlocking is done before notifying, to avoid waking up 
// the waiting thread only to block again (see notify_one for details) 
lk.unlock(); 
cv.notify_one(); 

你的榜樣然而,不前時,它應該使用notify_one()解鎖你的局部變量。你g()功能應該是這樣的:

void g() { 
    std::unique_lock<std::mutex> lock(mtx); 
    std::this_thread::sleep_for(std::chrono::seconds(1)); 
    lock.unlock(); 
    cv.notify_one(); 
} 
+0

我加入'''lock.unlock()'''後,我的代碼仍然有一個死鎖。 QAQ –

+0

然後你可能完全錯過了信號。你可能應該在每一行添加一些'cout'語句和'endl'。我敢打賭,「信號」線程在等待之前運行。您可以嘗試在啓動它們兩者之間進行睡眠以嘗試並保證執行順序 –

+0

@KevinAnderson - 不幸的是,插入I/O操作有時會改變計時,以至於最終隱藏問題。 –

0

創建,創建運行g()不保證f()將開始運行g()做之前線程之前運行f()線程。當g()首先啓動它抓住鎖,睡一秒鐘,然後通知的條件變量。由於沒有人正在等待條件,所以通知不起作用。當g()返回它釋放鎖。 然後f()獲取鎖並撥打wait()。沒有人把它喚醒,並且f()只是繼續等待。這不是一個僵局;任何線程仍然可以調用notify()並喚醒f()