2016-05-12 12 views
5

Non sono sicuro di aver davvero capito perché std::condition_variable ha bisogno di un ulteriore std::mutex come parametro? Non dovrebbe bloccarsi da solo?std :: condition_variable perché ha bisogno di uno 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(); 
} 

secondario, nell'esempio che sbloccare il mutex prima (signals metodo). Perché stanno facendo questo? Non dovrebbero prima bloccare e poi sbloccare dopo la notifica?

risposta

2

Una buona regola empirica da ricordare quando si lavora con più thread è che, quando si fa una domanda, il risultato potrebbe essere una bugia. Cioè, la risposta potrebbe essere cambiata da quando ti è stata data. L'unico modo per fare una domanda in modo affidabile è renderlo efficacemente a thread singolo. Inserisci mutex.

Una variabile di condizione attende un trigger per verificarne le condizioni. Per verificare le sue condizioni, ha bisogno di fare una domanda.

Se non si blocca prima di attendere, è possibile che si ponga la domanda e si ottenga la condizione, e si dice che la condizione è falsa. Questo diventa una bugia quando si verifica l'innesco e la condizione diventa vera. Ma non lo sai, dal momento che non esiste mutex che lo rende efficacemente a thread singolo.

Invece, si attende la variabile di condizione per un trigger che non scatterà mai, perché lo ha già fatto. Questo deadlock.

+1

Ok, grazie per questa spiegazione. Ma posso chiedere perché questo non è direttamente messo nell'implementazione di 'std :: condition_variable'? – Pascal

+0

Lo è. Ecco perché la funzione di attesa della variabile di condizione prende il blocco come parametro! –

4

Il mutex protegge il predicato, ovvero, la cosa che stai aspettando. Poiché la cosa che stai aspettando è, necessariamente, condivisa tra i thread, deve essere protetta in qualche modo.

Nell'esempio sopra riportato, i == 1 è il predicato. Il mutex protegge i.

Potrebbe essere utile fare un passo indietro e riflettere sul motivo per cui abbiamo bisogno di variabili di condizione. Un thread rileva uno stato che gli impedisce di avanzare in avanti e deve aspettare qualche altro thread per cambiare quello stato. Questo rilevamento dello stato deve avvenire sotto un mutex perché lo stato deve essere condiviso (altrimenti, come potrebbe un altro thread cambiare tale stato?).

Ma il thread non può rilasciare il mutex e quindi attendere. Cosa accadrebbe se lo stato fosse cambiato dopo che il mutex era stato rilasciato, ma prima che il thread riuscisse ad attendere? Quindi è necessaria un'operazione atomica di "sblocco e attesa". Questo è specificamente ciò che le variabili condizionali forniscono.

Senza mutex, cosa sbloccherebbero?

La scelta se segnalare la variabile di condizione prima o dopo aver rilasciato il blocco è complessa con vantaggi su entrambi i lati.

+1

Bella spiegazione! –