2017-10-06 116 views
2

我發現這個code在代碼審查堆棧交換,它實現了生產者 - 消費者問題。我在這裏發佈一段代碼。在同一個互斥量上使用兩個std :: unique_lock會導致死鎖?

在給定的代碼,讓我們考慮一個場景時,製片人致電void add(int num)產生價值,它獲取的是互斥鎖mubuffer.size()==size_這使得在等待隊列中的生產者進入,由於條件變量cond

同時發生上下文切換並且消費者調用函數int remove()消耗值,它嘗試獲取互斥鎖mu上的鎖,但鎖由生產者以前已經獲取,因此它失敗並且永不消耗該值,從而導致死鎖。

我在哪裏錯了?因爲代碼似乎在我運行時正常工作,調試它並沒有幫助我。

感謝

void add(int num) { 
     while (true) { 
      std::unique_lock<std::mutex> locker(mu); 
      cond.wait(locker, [this](){return buffer_.size() < size_;}); 
      buffer_.push_back(num); 
      locker.unlock(); 
      cond.notify_all(); 
      return; 
     } 
    } 
    int remove() { 
     while (true) 
     { 
      std::unique_lock<std::mutex> locker(mu); 
      cond.wait(locker, [this](){return buffer_.size() > 0;}); 
      int back = buffer_.back(); 
      buffer_.pop_back(); 
      locker.unlock(); 
      cond.notify_all(); 
      return back; 
     } 
    } 
+0

等待應釋放鎖定,直到條件滿足並在退出等待之前重新獲取它 –

+1

'while(true){..;返回;}'...爲什麼使用循環? – Jarod42

回答

3

OutOfBound的答案很好,但有關「原子」究竟是什麼的更多細節是有用的。

對條件變量的wait操作有一個先決條件和後置條件,即調用者鎖定傳入的互斥鎖。 wait操作可以在內部解鎖互斥鎖,並且可以保證不會錯過任何由於解鎖互斥鎖而發生的其他線程的notifynotify_all操作。在wait裏面,互斥鎖的解鎖和進入等待通知的狀態是相對於原子的。這可以避免睡眠/喚醒比賽。

條件臨界區形式在內部測試謂詞。但它依然依靠通知正確完成。

從某種意義上說,一個能想到的wait因爲這樣做:

while (!predicate()) { 
    mutex.unlock(); 
    /* sleep for a short time or spin */ 
    mutex.lock(); 
} 

條件變量與通知允許中間的註釋行是有效的。其中給出:

while (!predicate()) { 
    atomic { /* This is the key part. */ 
     mutex.unlock(); 
     sleep_until_notified(); 
    } 
    mutex.lock(); 
} 
2

的想法std::condition_variable::wait(lock, predicate),是您在等待謂詞得到滿足,並且對互斥鎖之後。要以原子方式執行此操作(大部分時間都很重要),必須先鎖定互斥鎖,然後等待釋放並鎖定它以檢查謂詞。如果滿足,互斥鎖保持鎖定狀態並繼續執行。否則,互斥體將再次釋放。

相關問題