2016-05-12 9 views
5

Ich bin mir nicht sicher, ob ich wirklich verstehe, warum std::condition_variable einen zusätzlichen std::mutex als Parameter benötigt? Sollte es sich nicht selbst blockieren?std :: condition_variable warum benötigt es ein std :: mutex

#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(); 
} 

Secondary im Beispiel entriegeln sie den Mutex ersten (signals Methode). Warum machen sie das? Können sie nicht zuerst sperren und dann nach der Benachrichtigung entsperren?

Antwort

2

Eine gute Faustregel bei der Arbeit mit mehreren Threads ist, dass das Ergebnis eine Lüge sein kann, wenn Sie eine Frage stellen. Das heißt, die Antwort hat sich möglicherweise geändert, seit es Ihnen gegeben wurde. Die einzige Möglichkeit, eine Frage zuverlässig zu stellen, ist, sie effektiv single-threaded zu machen. Geben Sie Mutexe ein.

Eine Zustandsvariable wartet auf einen Trigger, damit dieser seinen Zustand prüfen kann. Um seinen Zustand zu überprüfen, muss er eine Frage stellen.

Wenn Sie vor dem Warten nicht sperren, ist es möglich, dass Sie die Frage stellen und die Bedingung erhalten, und Ihnen wird gesagt, dass die Bedingung falsch ist. Dies wird zur Lüge, wenn der Auslöser auftritt und die Bedingung wahr wird. Aber Sie wissen das nicht, da es keinen Mutex gibt, der das effektiv single-threaded macht.

Stattdessen warten Sie auf die Bedingung Variable für einen Trigger, der nie ausgelöst wird, weil es bereits getan hat. Diese Deadlocks.

+1

Ok, danke für diese Erklärung. Aber darf ich fragen, warum dies nicht direkt in die Implementierung der 'std :: condition_variable' gebracht wird? – Pascal

+0

Es ist. Deshalb übernimmt die Wartefunktion der Zustandsvariablen die Sperre als Parameter! –

4

Der Mutex schützt das Prädikat, das ist die Sache, auf die Sie warten. Da das Ding, auf das Sie warten, notwendigerweise zwischen Threads geteilt wird, muss es irgendwie geschützt werden.

In Ihrem obigen Beispiel ist i == 1 das Prädikat. Der Mutex schützt i.

Es kann hilfreich sein, einen Schritt zurückzutreten und darüber nachzudenken, warum wir Zustandsvariablen benötigen. Ein Thread erkennt einen Zustand, der den Fortschritt verhindert, und muss auf einen anderen Thread warten, um diesen Status zu ändern. Diese Erkennung des Zustands muss unter einem Mutex stattfinden, da der Status geteilt werden muss (andernfalls könnte ein anderer Thread diesen Zustand ändern?).

Aber der Thread kann den Mutex nicht freigeben und dann warten. Was ist, wenn sich der Zustand nach der Veröffentlichung des Mutex geändert hat, bevor der Thread warten konnte? Sie benötigen also eine atomare "Entsperren und Warten" -Operation. Genau das bieten Zustandsvariablen.

Ohne Mutex, was würden sie entsperren?

Die Auswahl, ob die Zustandsvariable vor oder nach dem Lösen der Sperre angezeigt werden soll, ist komplex und hat auf beiden Seiten Vorteile.

+1

Schöne Erklärung! –