2016-05-12 119 views
5

我不知道如果我真的明白爲什麼std::condition_variable需要額外std::mutex作爲參數?它應該不是由自己鎖定?的std :: condition_variable爲什麼它需要一個std ::互斥

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

std::condition_variable cv; 
std::mutex cv_m; 
int i = 0; 
bool done = false; 

void waits() 
{ 
    std::unique_lock<std::mutex> lk(cv_m); 
    std::cout << "Waiting... \n"; 
    cv.wait(lk, []{return i == 1;}); 
    std::cout << "...finished waiting. i == 1\n"; 
    done = true; 
} 

void signals() 
{ 
    std::this_thread::sleep_for(std::chrono::seconds(1)); 
    std::cout << "Notifying falsely...\n"; 
    cv.notify_one(); // waiting thread is notified with i == 0. 
        // cv.wait wakes up, checks i, and goes back to waiting 

    std::unique_lock<std::mutex> lk(cv_m); 
    i = 1; 
    while (!done) 
    { 
     std::cout << "Notifying true change...\n"; 
     lk.unlock(); 
     cv.notify_one(); // waiting thread is notified with i == 1, cv.wait returns 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 
     lk.lock(); 
    } 
} 

int main() 
{ 
    std::thread t1(waits), t2(signals); 
    t1.join(); 
    t2.join(); 
} 

二次,在例如它們解鎖互斥第一(signals方法)。他們爲什麼這樣做? Shoulden't他們首先鎖定,然後通知後解鎖?

回答

2

拇指與多線程工作時要記住的一個很好的規則是,當你問一個問題,結果可能是一個謊言。也就是說,答案可能已經改變,因爲它是給你的。可靠地提出問題的唯一方法是使其實現單線程化。輸入互斥體。

條件變量等待觸發器,以便它可以檢查其條件。要檢查它的狀況,它需要提出一個問題。

如果你不等待前鎖定,那麼,你問的問題,並得到了條件,你被告知,條件爲假是可能的。當觸發發生並且條件成立時,這成爲謊言。但是你不知道這一點,因爲沒有互斥體使這個有效的單線程。

相反,你等上一個觸發器,它永遠不會在條件變量,因爲它已經做到了。這個僵局。

+1

好的,謝謝你的解釋。但是,我可以問爲什麼這不是直接投入到'std :: condition_variable'的實現? – Pascal

+0

就是這樣。這就是條件變量的等待函數將鎖作爲參數的原因! –

4

互斥量保護謂詞,也就是說,您正在等待的東西。既然你正在等待的東西,必然是在線程之間共享的,它必須以某種方式受到保護。

在你上面的例子,i == 1是謂語。該互斥鎖保護i

退一步考慮爲什麼我們需要條件變量可能會有幫助。一個線程檢測到阻止其前進的狀態,並需要等待其他線程更改該狀態。這種狀態檢測必須在互斥體下進行,因爲狀態必須共享(否則,另一個線程如何改變該狀態?)。

但是線程無法釋放互斥鎖,然後等待。如果在互斥鎖釋放之後狀態發生了變化,但在線程設法等待之前該怎麼辦?所以你需要一個原子的「解鎖和等待」操作。這就是特定條件變量提供的條件。

沒有互斥鎖,它們會解鎖什麼?

的前或釋放鎖後是否用信號的條件變量的選擇是一個複雜的,兩側的優點。

+1

很好的解釋! –

相關問題