2013-06-02 17 views
16

Stavo leggendo attraverso un tutorial Boost Mutex su drdobbs.com, e ho trovato questo pezzo di codice:Boost Mutex con mirino Blocco

#include <boost/thread/thread.hpp> 
#include <boost/thread/mutex.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 

boost::mutex io_mutex; 

void count(int id) 
{ 
    for (int i = 0; i < 10; ++i) 
    { 
    boost::mutex::scoped_lock 
     lock(io_mutex); 
    std::cout << id << ": " << 
     i << std::endl; 
    } 
} 

int main(int argc, char* argv[]) 
{ 
    boost::thread thrd1(
    boost::bind(&count, 1)); 
    boost::thread thrd2(
    boost::bind(&count, 2)); 
    thrd1.join(); 
    thrd2.join(); 
    return 0; 
} 

Ora capisco il punto di un mutex è quello di evitare che due thread di accedere al stessa risorsa allo stesso tempo, ma non vedo la correlazione tra io_mutex e std :: cout. Questo codice blocca tutto all'interno dell'ambito finché l'ambito non è finito?

+5

Sì, 'scoped_lock' blocca il mutex fino all'uscita dall'ambito. Ha un nome piuttosto intuitivo. – juanchopanza

+0

@juanchopanza Capisco che la parte, scoped_lock ha perfettamente senso. Quello che non capisco è la relazione tra il mutex e qualsiasi altra cosa nel programma. È più legato all'ordine di esecuzione nel programma e meno alla memoria effettiva? Scusate se si tratta di una domanda noob, ma ho cercato di imparare i mutex per la settimana scorsa o poco più e non lo capisco. –

+0

@ jlw2387: Come ho scritto nella mia risposta, 'std :: cout' è un oggetto globale, quindi puoi vederlo come una risorsa condivisa. L'accesso a una risorsa condivisa contemporaneamente da thread diversi richiede la sincronizzazione. Questo è ciò che sta facendo 'scoped_lock ' –

risposta

20

Ora capisco che il punto di un Mutex è impedire a due thread di accedere alla stessa risorsa nello stesso momento, ma non vedo la correlazione tra io_mutex e std :: cout.

std::cout è un oggetto globale, in modo da poter vedere che come una risorsa condivisa. Se si accede simultaneamente da più thread, tali accessi devono essere sincronizzati in qualche modo, per evitare rotture di dati e comportamenti non definiti.

Forse sarà più facile per farvi notare che l'accesso simultaneo avviene per ritenere che:

std::cout << x 

è in realtà equivale a:

::operator << (std::cout, x) 

Il che significa che si sta chiamando una funzione che opera su l'oggetto std::cout e lo si sta facendo da diversi thread contemporaneamente. std::cout deve essere protetto in qualche modo. Ma non è questa la sola ragione per cui lo scoped_lock è lì (continua a leggere).

Questo codice blocca tutto all'interno dell'oscilloscopio finché l'oscilloscopio non è finito?

Sì, blocca io_mutex finché l'oggetto serratura stessa esula dall'ambito (essendo un tipico involucro Raii), che avviene al termine di ogni iterazione del ciclo.

Perché è necessario? Bene, anche se in C++ 11 gli inserimenti individuali in cout sono garantiti come thread-safe, gli inserimenti successivi possono essere intercalati quando più thread stanno emettendo qualcosa.

Tenete a mente che ogni inserimento attraverso operator << è una chiamata di funzione separata, come se si stesse facendo:

std::cout << id; 
std::cout << ": "; 
std::cout << i; 
std::cout << endl; 

Il fatto che operator << restituisce l'oggetto stream permette di concatenare la funzione precedente chiamate in un unico espressione (come hai fatto nel tuo programma), ma il fatto che tu stia avendo diverse chiamate di funzioni separate rimane valido.

Ora guardando frammento di sopra, è più evidente che lo scopo di questo ambito di blocco è quello di fare in modo che ogni messaggio del tipo:

<id> ": " <index> <endl> 

viene stampato senza le sue parti che sono intercalati con parti di altri messaggi.

Inoltre C++ 03 (dove inserimenti in cout sono non garantiti per essere thread-safe), la serratura proteggerà la stessa oggetto da cui si accede contemporaneamente cout.

+0

Non sono sicuro di aver chiarito che il mutex blocca una regione critica di codice --- un percorso di esecuzione e _non_ qualsiasi risorsa o oggetto. Per quanto riguarda la sicurezza del thread di '>>' in pre-C++ 11: dipende dall'implementazione. Alcuni erano thread-safe. E sono piuttosto sorpreso della tua affermazione che 'std :: cout << i' è thread-safe in C++ 11. Non riesco a vedere alcun motivo per renderlo tale. (Non riesco nemmeno a trovare il punto in cui lo standard lo garantisce.) Ma non so neanche dove guardare.) –

+1

@JamesKanze: Questo è specificato nel §27.4.1/4. –

+0

Mi chiedo perché. Non ti compra nulla (come giustamente fai notare), e va contro i principi generali usati altrove nella biblioteca. (Un approccio più utile sarebbe stato quello di fornire un membro mutex dello stream, in modo che anche le librerie senza conoscenza reciproca potessero concordare sul mutex da utilizzare.) –

7

Un mutex non ha nulla a che fare con qualcos'altro nel programma (eccetto una variabile condizionale), almeno ad un livello superiore. Un mutex ha due effetti: controlla il flusso del programma e impedisce a più thread di eseguire contemporaneamente lo stesso blocco di codice . Assicura anche la sincronizzazione della memoria. Il problema importante di qui, è che i mutex non sono associati alle risorse e non impediscono a due thread di accedere alla stessa risorsa allo stesso tempo. Un mutex definisce una sezione critica di codice, che può essere inserita solo da un thread alla volta. Se l'utilizzo di una particolare risorsa viene eseguito nelle sezioni critiche controllate dallo stesso mutex, la risorsa è protetta in modo efficace dal mutex. Ma la relazione è stabilita dal codificatore, assicurando che tutti gli utilizzi occupino il posto nelle sezioni critiche.

Problemi correlati