C ++ 17 ha introdotto una nuova classe di blocco denominata std::scoped_lock
.
A giudicare dalla documentazione sembra simile alla std::lock_guard
classe già esistente .
Qual è la differenza e quando dovrei usarla?
C ++ 17 ha introdotto una nuova classe di blocco denominata std::scoped_lock
.
A giudicare dalla documentazione sembra simile alla std::lock_guard
classe già esistente .
Qual è la differenza e quando dovrei usarla?
Risposte:
La scoped_lock
è una versione strettamente superiore lock_guard
che 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_guard
esiste 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).
lock_guard
. Ma certamente rende le classi di guardia un po 'più facili da usare.
La sola e importante differenza è che std::scoped_lock
un 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::lock
come spiegato questa risposta .
L'aggiunta del blocco dell'ambito rende questo più facile da usare ed evita i relativi errori. Puoi considerare std::lock_guard
deprecato. Il caso di argomento singolo di std::scoped_lock
può 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_lock
quale può essere visto qui .
Per ulteriori informazioni, potresti voler leggere il documento standard
scoped_lock lk; // locks all mutexes in scope
. LGTM.
scoped_lock lk;
è la nuova scorciatoia per scoped_lock<> lk;
. Non ci sono mutex. Quindi hai ragione. ;-)
Risposta tardiva e principalmente in risposta a:
Puoi considerare
std::lock_guard
deprecato.
Per il caso comune che uno ha bisogno di bloccare esattamente un mutex, std::lock_guard
ha 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_guard
stato 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:
lock_guard
se è necessario bloccare esattamente 1 mutex per un intero ambito.
scoped_lock
se è necessario bloccare un numero di mutex non esattamente 1.
unique_lock
se è necessario sbloccare nell'ambito del blocco (che include l'uso con a condition_variable
).
Questo consiglio non non implica che scoped_lock
debba essere riprogettato di non accettare 0 mutex. Esistono casi d'uso validi in cui è desiderabile scoped_lock
accettare pacchetti di parametri del modello variadic che possono essere vuoti. E la custodia vuota non dovrebbe bloccare nulla.
Ed è per questo che lock_guard
non è 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).
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_lock
significa che la maggior parte dei casi in cui avresti usatostd::lock
prima di c ++ 17 ora può essere scritta usandostd::scoped_lock
, con meno possibilità di errori, il che può essere solo una buona cosa!