std :: lock_guard o std :: scoped_lock?


Risposte:


125

La scoped_lockè una versione strettamente superiore lock_guardche blocca un numero arbitrario di mutex tutti contemporaneamente (usando lo stesso algoritmo deadlock-avoidance come std::lock). Nel nuovo codice, dovresti sempre e solo usare scoped_lock.

L'unica ragione lock_guardesiste ancora è per la compatibilità. Non può essere semplicemente eliminato, poiché viene utilizzato nel codice corrente. Inoltre, si è rivelato indesiderabile cambiare la sua definizione (da unaria a variadica), poiché si tratta anche di un cambiamento osservabile, e quindi di rottura, (ma per ragioni alquanto tecniche).


8
Inoltre, grazie alla deduzione dell'argomento del modello di classe, non è nemmeno necessario elencare i tipi bloccabili.
Nicol Bolas,

3
@NicolBolas: è vero, ma vale anche per lock_guard. Ma certamente rende le classi di guardia un po 'più facili da usare.
Kerrek SB,

6
scoped_lock è solo C ++ 17
Shital Shah,

1
Essendo c ++ 17, la compatibilità è un motivo particolarmente valido per la sua esistenza. Sono anche in disaccordo con veemenza con qualsiasi affermazione assolutista di "dovresti sempre usare" quando l'inchiostro si sta ancora asciugando da questo standard.
Paul Childs,

88

La sola e importante differenza è che std::scoped_lockun costruttore variadico prende più di un mutex. Ciò consente di bloccare più mutex in un deadlock evitando di usarli std::lock.

{
    // safely locked as if using std::lock
    std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);     
}

In precedenza dovevi fare una piccola danza per bloccare più mutex in modo sicuro usando std::lockcome spiegato questa risposta .

L'aggiunta del blocco dell'ambito rende questo più facile da usare ed evita i relativi errori. Puoi considerare std::lock_guarddeprecato. Il caso di argomento singolo di std::scoped_lockpuò essere implementato come una specializzazione e tale non devi temere di possibili problemi di prestazioni.

GCC 7 ha già il supporto per il std::scoped_lockquale può essere visto qui .

Per ulteriori informazioni, potresti voler leggere il documento standard


9
Ha risposto alla tua domanda dopo soli 10 minuti. Davvero non lo sapevi?
Walter,


3
Quando l'ho sollevato in commissione, la risposta era "niente". Può darsi che il caso degenerato di qualche algoritmo, questa sia esattamente la cosa giusta. Oppure potrebbe essere che un numero sufficiente di persone non blocchi accidentalmente nulla quando intendono bloccare qualcosa è un problema comune. Non ne sono davvero sicuro.
Howard Hinnant,

3
@HowardHinnant: scoped_lock lk; // locks all mutexes in scope. LGTM.
Kerrek SB,

2
@KerrekSB: scoped_lock lk;è la nuova scorciatoia per scoped_lock<> lk;. Non ci sono mutex. Quindi hai ragione. ;-)
Howard Hinnant,

26

Risposta tardiva e principalmente in risposta a:

Puoi considerare std::lock_guarddeprecato.

Per il caso comune che uno ha bisogno di bloccare esattamente un mutex, std::lock_guardha un'API che è un po 'più sicura da usare di scoped_lock.

Per esempio:

{
   std::scoped_lock lock; // protect this block
   ...
}

Il frammento di cui sopra è probabilmente un errore di runtime accidentale perché si compila e quindi non fa assolutamente nulla. Il programmatore probabilmente significava:

{
   std::scoped_lock lock{mut}; // protect this block
   ...
}

Ora blocca / sblocca mut.

Se invece è lock_guardstato utilizzato nei due esempi precedenti, il primo esempio è un errore di compilazione anziché un errore di runtime e il secondo esempio ha funzionalità identiche alla versione che utilizza scoped_lock.

Quindi il mio consiglio è di usare lo strumento più semplice per il lavoro:

  1. lock_guard se è necessario bloccare esattamente 1 mutex per un intero ambito.

  2. scoped_lock se è necessario bloccare un numero di mutex non esattamente 1.

  3. unique_lockse è necessario sbloccare nell'ambito del blocco (che include l'uso con a condition_variable).

Questo consiglio non non implica che scoped_lockdebba essere riprogettato di non accettare 0 mutex. Esistono casi d'uso validi in cui è desiderabile scoped_lockaccettare pacchetti di parametri del modello variadic che possono essere vuoti. E la custodia vuota non dovrebbe bloccare nulla.

Ed è per questo che lock_guardnon è deprecato. scoped_lock e unique_lock può essere un superset di funzionalità di lock_guard, ma quel fatto è un'arma a doppio taglio. A volte è altrettanto importante ciò che un tipo non farà (costrutto predefinito in questo caso).


13

Ecco un esempio e una citazione dalla concorrenza C ++ in azione :

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == & rhs)
        return;
    std::lock(lhs.m, rhs.m);
    std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
    std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
    swap(lhs.some_detail, rhs.some_detail);
}

vs.

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == &rhs)
        return;
    std::scoped_lock guard(lhs.m, rhs.m);
    swap(lhs.some_detail, rhs.some_detail);
}

L'esistenza di std::scoped_locksignifica che la maggior parte dei casi in cui avresti usato std::lockprima di c ++ 17 ora può essere scritta usando std::scoped_lock, con meno possibilità di errori, il che può essere solo una buona cosa!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.