2016-05-12 15 views
5

Nie jestem pewien, czy naprawdę rozumiem, dlaczego std::condition_variable potrzebuje dodatkowego std::mutex jako parametru? Czy nie powinien sam się blokować?std :: condition_variable dlaczego potrzebuje 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(); 
} 

wtórne, w tym przykładzie, że odblokować MUTEX pierwszy (signals metody). Czemu oni to robią? Nie powinny one najpierw blokować, a następnie odblokowywać po powiadomieniu?

Odpowiedz

2

Dobrą zasadą do zapamiętania podczas pracy z wieloma wątkami jest to, że gdy zadajesz pytanie, wynik może być kłamstwem. To znaczy, odpowiedź mogła ulec zmianie, ponieważ została ci dana. Jedynym sposobem, aby rzetelnie zadać pytanie, jest sprawienie, by był on efektywny jednowątkowo. Wprowadź muteksy.

Zmienna warunku czeka na wyzwalacz, aby mógł sprawdzić jego stan. Aby sprawdzić jego stan, musi zadać pytanie.

Jeśli nie zablokujesz przed czekaniem, możliwe, że zadasz pytanie i uzyskasz warunek, a użytkownik zostanie poinformowany, że warunek jest fałszywy. To staje się kłamstwem, gdy pojawia się wyzwalacz i warunek staje się prawdziwy. Ale nie wiesz o tym, ponieważ nie ma mutexu, który sprawiłby, że byłby efektywny jednowątkowy.

Zamiast tego należy czekać na zmienną warunku dla wyzwalacza, który nigdy się nie uruchomi, ponieważ już to zrobił. Te zakleszczenia.

+1

Dobrze, dzięki za wyjaśnienie. Ale czy mogę zapytać, dlaczego nie jest to bezpośrednio wprowadzone w implementację 'std :: condition_variable'? – Pascal

+0

To jest. Dlatego funkcja oczekiwania zmiennej warunkowej przyjmuje blokadę jako parametr! –

4

Muteks chroni predykat, czyli to, na co czekasz. Ponieważ rzecz, na którą czekasz, jest koniecznie dzielona między wątki, musi być jakoś zabezpieczona.

W powyższym przykładzie i == 1 jest predykatem. Mutex chroni i.

Pomocne może być cofnięcie się i zastanowienie się, dlaczego potrzebujemy zmiennych warunkowych. Jeden wątek wykrywa stan, który uniemożliwia mu wykonanie postępu i musi poczekać, aż inny wątek zmieni ten stan. To wykrywanie stanu musi odbywać się w muteksie, ponieważ stan musi być współużytkowany (w przeciwnym razie, jak inny wątek mógłby zmienić ten stan?).

Ale wątek nie może zwolnić muteksu, a następnie czekać. Co się stanie, jeśli stan się zmieni po zwolnieniu muteksu, ale zanim wątek będzie w stanie poczekać? Potrzebujesz więc atomowej operacji "odblokuj i czekaj". Właśnie to zapewniają zmienne warunkowe.

Bez muteksu, co by odblokować?

Wybór, czy zasygnalizować zmienną warunku przed zwolnieniem blokady, czy po niej, jest złożony i ma zalety po obu stronach.

+1

Niezłe wytłumaczenie! –