C ++ 17 ha introdotto una nuova classe di blocco denominata std::scoped_lock.
A giudicare dalla documentazione sembra simile alla std::lock_guardclasse 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_guardclasse già esistente .
Qual è la differenza e quando dovrei usarla?
Risposte:
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).
lock_guard. Ma certamente rende le classi di guardia un po 'più facili da usare.
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
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_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:
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_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).
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 usatostd::lockprima di c ++ 17 ora può essere scritta usandostd::scoped_lock, con meno possibilità di errori, il che può essere solo una buona cosa!