2017-04-22 81 views
0

以下是一些將從多個線程調用的代碼片段。每個線程的謂詞std::condition_variable::wait會有所不同,但它不會改變問題。訂購std :: unique_lock <std::mutex>後多重線程如何重新獲取std :: condition_variable :: notify_all

std::unique_lock<std::mutex> lock{connection_mutex}; 
cv.wait(lock, 
    [conn = shared_from_this()] 
    { 
     return conn->connection_is_made(); 
    } 
); 

//do some stuff 
lock.unlock(); 

我在cppreference

閱讀此當條件變量被通知,超時期滿,或發生虛假喚醒,該線程被喚醒,並且互斥原子重新獲取。如果喚醒是虛假的,那麼線程應該檢查條件並重新等待。

從這個我明白,lock將被鎖定,比謂詞拉姆達將被檢查,如果返回truelock將保持鎖定狀態,我可以繼續做這個互斥鎖的保護下一些東西。當std::condition_variablenotify_one()通知成員函數時,它是非常有意義的。

但是當std::condition_variablenotify_all()通知時會發生什麼?我不能在文檔中找到所有線程是否被喚醒,而不是「等待在一行中」來鎖定互斥鎖,然後檢查謂詞返回的內容,或者可能還有其他操作。


編輯:我看到下面的評論後開始思考一點。 std::condition_variable::wait預計std::unique_lock作爲第一個參數,當std::condition_variable::wait在通知後被喚醒時 - std::unique_lock將被重新獲取。現在,如果多個線程正在等待相同的通知,那麼當notify_all()最終被調用特定的std::condition_variable時,只有1個線程將能夠鎖定互斥鎖,並且所有其他線程將回到休眠狀態。因此,除非效率較低,否則,如果它具有與notify_one()相同的效果,則根本沒有看到具有notify_all()成員函數的任何點。我的意思是,如果必須重新獲取互斥量爲 的線程才能通過std::condition_variable::wait,那麼所有等待的線程都不能同時執行。

+0

如果您詢問通知訂單,那麼這可能會有所幫助:http:// stackoverflow。com/questions/15912322/condition-variable-deadlock – didiz

+0

感謝您的回答,但在這種情況下,我並不擔心從不接收notify_all()信號。我只想知道多個線程在等待相同的condition_variable時是如何工作的,然後通過notify_all()接收通知,然後嘗試獲取相同的互斥鎖。 – etrusks

+2

這取決於操作系統調度程序。您無法知道哪個線程首先獲取互斥鎖。 – 1000ml

回答

0

我做了一些測試,看看是否所有的線程在notify_all()調用後都會醒來,即使是那些沒有被安排爲第一次喚醒的線程。

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

std::mutex M; 
std::condition_variable CV; 
int var = 0; 


int main() 
{ 
    std::thread t1{ 
     []{ 
      std::unique_lock<std::mutex> lck{M}; 
      while(var != 1){ 
       CV.wait(lck); 
       std::cout << "t1 woken up and (i != 1) = " 
          << (var != 1) << std::endl; 
      } 
     } 
    }; 

    std::thread t2{ 
     []{ 
      std::unique_lock<std::mutex> lck{M}; 
      while(var != 2){ 
       CV.wait(lck); 
       std::cout << "t2 woken up and (i != 2) = " 
          << (var != 2) << std::endl; 
      } 
     } 
    }; 

    std::thread t3{ 
     []{ 
      std::unique_lock<std::mutex> lck{M}; 
      while(var != 3){ 
       CV.wait(lck); 
       std::cout << "t3 woken up and (i != 3) = " 
          << (var != 3) << std::endl; 
      } 
     } 
    }; 

    std::thread t4{ 
     []{ 
      std::unique_lock<std::mutex> lck{M}; 
      while(var != 4){ 
       CV.wait(lck); 
       std::cout << "t4 woken up and (i != 4) = " 
          << (var != 4) << std::endl; 
      } 
     } 
    }; 

    for(int i = 0; i < 6; ++i){ 
     std::unique_lock<std::mutex> lck{M}; 
     var = i; 
     CV.notify_all(); 
     lck.unlock(); 
     std::this_thread::sleep_for(std::chrono::seconds{1}); 
     std::cout << "\n\n"; 
    } 

    t1.join(); 
    t2.join(); 
    t3.join(); 
    t4.join(); 
} 

而且這裏的結果

t3 woken up and (i != 3) = 1 //spurious wakeup 


t3 woken up and (i != 3) = 1 
t4 woken up and (i != 4) = 1 
t2 woken up and (i != 2) = 1 
t1 woken up and (i != 1) = 0 


t3 woken up and (i != 3) = 1 
t4 woken up and (i != 4) = 1 
t2 woken up and (i != 2) = 0 


t3 woken up and (i != 3) = 0 
t4 woken up and (i != 4) = 1 


t4 woken up and (i != 4) = 0 

所以我很高興地看到,不管是什麼的線程被喚醒月1日,所有的通知線程都吵醒了一些隨機的順序與單致電notify_all()。看起來像是當notify_all()(稱爲線程A)被喚醒線程中的一個在做了一些工作之後解鎖mutex時,下一個被線程A調用爲notify_all()的線程被自動鎖定相同的mutex線程A剛剛解鎖。這一直持續到notify_all()被喚醒的所有線程鎖定/解鎖用於保護共享數據的相同mutex

相關問題