2013-07-18 12 views
5

Domanda semplice - in pratica, devo sbloccare un mutex, o posso semplicemente usare gli operatori dell'oscilloscopio e il mutex si sbloccherà automaticamente?Un mutex si sblocca quando esce dall'oscilloscopio?

cioè:

{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++; 
} // my mutex is now unlocked? 

o dovrei:

{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++; 
    pthread_mutex_unlock (&myMutex); 
} 
+5

Quale implementazione mutex stai usando? A meno che tu non stia implementando mutex con RAII, probabilmente dovrai sbloccare esplicitamente il mutex. – Void

+0

Grazie a @Void. Sto usando pthread.h. Cioè pthread_mutex_unlock() e pthread_mutex_lock(). Cos'è RAII? –

+3

@AmitNayar: vedere http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization.È probabilmente l'idioma più importante in C++, poiché è quasi impossibile gestire correttamente la memoria e altre risorse dinamiche senza di esso. –

risposta

16

Il mutex non sta andando fuori ambito nei vostri esempi; e non c'è modo per il compilatore di sapere che una particolare funzione ha bisogno di chiamare alla fine dell'ambito, quindi il primo esempio non è non sbloccare il mutex.

Se si utilizzano le funzioni (a rischio di errore) per bloccare e sbloccare il mutex, è necessario assicurarsi di chiamare sempre unlock() - anche se l'operazione protetta genera un'eccezione.

Il modo migliore per farlo è quello di utilizzare una classe RAII per gestire il blocco, come si farebbe per qualsiasi altra risorsa che ha bisogno di rilasciare dopo l'uso:

class lock_guard { 
public: 
    explicit lock_guard(mutex & m) : m(m) {mutex_lock(m);} 
    ~lock_guard() {mutex_unlock(m);} 

    lock_guard(lock_guard const &) = delete; 
    void operator=(lock_guard &) = delete; 

private: 
    mutex & m; 
}; 

// Usage 
{ 
    lock_guard lock(myMutex); 
    shared_resource++; 
} // mutex is unlocked here (even if an exception was thrown) 

Nella moderna C++, utilizzare std::lock_guard o std::unique_lock per Questo.

2

Utilizzando il metodo di applicazione Raii è molto migliore perché garantisce che il mutex sarà sempre sbloccato anche a fronte di eccezioni o primi ritorno.

Se si ha accesso a C++ 11, si potrebbe prendere in considerazione l'uso di , nel qual caso non è necessario bloccarlo per incrementare.

2

In questo caso, no il mutex non verrà sbloccato quando questo codice non rientra nell'ambito.

I locker di mutex che seguono RAII utilizzano il fatto che un distruttore viene chiamato automaticamente quando un oggetto allocato non heap esce dall'ambito. Quindi sblocca il mutex una volta che l'oggetto che ha bloccato il mutex esce dall'ambito. Nel caso del codice, nessun oggetto viene allocato nell'ambito delle parentesi graffe, quindi non è possibile sbloccare il mutex una volta terminato l'ambito.

Ad esempio, utilizzando QMutexLocker dalle librerie Qt, è possibile garantire che il vostro mutex è sbloccato quando la portata è finita:

{ 
    QMutexLocker locker(myMutex); 
    if(checkSomething()) 
    { 
     return; 
    } 
    doSomething(); 
} 

Questo codice è simile a:

{ 
    mutex_lock(myMutex); 
    if(checkSomething()) 
    { 
     mutex_unlock(myMutex); 
     return; 
    } 
    doSomething(); 
    mutex_unlock(myMutex); 
} 

Anche se, come Brian Neal sottolinea che non gestisce in modo sicuro il caso in cui checkSomething() e doSomething() generano eccezioni.

Un'alternativa a Qt's QMutexLocker sarebbe STD std::lock_guard.

+2

I due frammenti di codice non sono equivalenti se si considerano le eccezioni. Per renderli equivalenti dovresti aggiungere i blocchi try/catch attorno a 'checkSomething' e' doSomething'. Un altro motivo per preferire il tuo primo esempio che utilizza RAII. :) –

+0

Buon punto, @ BrianNeal. Ho aggiornato la risposta. Faccio un punto a non usare le eccezioni, poiché non sono realmente necessarie con l'infrastruttura di 'Qt', quindi facilmente dimentico che altri devono tenerne conto. –

Problemi correlati