La diferencia es que puedes bloquear y desbloquear a std::unique_lock
. std::lock_guard
será bloqueado solo una vez en construcción y desbloqueado en destrucción.
Entonces, para el caso de uso B, definitivamente necesita un std::unique_lock
para la variable de condición. En el caso A, depende de si necesita volver a bloquear la guardia.
std::unique_lock
tiene otras características que le permiten, por ejemplo: construirse sin bloquear el mutex inmediatamente, pero construir el contenedor RAII (ver aquí ).
std::lock_guard
también proporciona un conveniente contenedor RAII, pero no puede bloquear múltiples mutexes de manera segura. Se puede usar cuando necesita un contenedor para un alcance limitado, por ejemplo: una función miembro:
class MyClass{
std::mutex my_mutex;
void member_foo() {
std::lock_guard<mutex_type> lock(this->my_mutex);
/*
block of code which needs mutual exclusion (e.g. open the same
file in multiple threads).
*/
//mutex is automatically released when lock goes out of scope
};
Para aclarar una pregunta por chmike, por defecto std::lock_guard
y std::unique_lock
son lo mismo. Entonces, en el caso anterior, podría reemplazar std::lock_guard
con std::unique_lock
. Sin embargo, std::unique_lock
podría tener un poco más de gastos generales.
Tenga en cuenta que en estos días se debe usar en std::scoped_lock
lugar de std::lock_guard
.
std::lock_guard
es suficiente para su caso A, entonces debe usarlo. No solo evita gastos innecesarios, sino que también muestra la intención al lector de que nunca desbloqueará este protector.unique_lock
es probable que la sobrecarga adicional de la misma se vea reducida por el costo de realmente bloquear y desbloquear el mutex (si el compilador no optimizó esa sobrecarga, lo que podría ser posible).So for usecase B you definitely need a std::unique_lock for the condition variable
- Sí, pero solo en el hilo quecv.wait()
es, porque ese método libera atómicamente el mutex. En el otro hilo donde actualiza la (s) variable (s) compartida (s) y luego llamacv.notify_one()
, un simple eslock_guard
suficiente para bloquear el mutex dentro del alcance ... ¡a menos que esté haciendo algo más elaborado que no puedo imaginar! por ejemplo, en.cppreference.com/w/cpp/thread/condition_variable - funciona para mí :)lock_guard
yunique_lock
son más o menos lo mismo;lock_guard
es una versión restringida con una interfaz limitada.A
lock_guard
siempre tiene un candado desde su construcción hasta su destrucción. Aunique_lock
se puede crear sin bloqueo inmediato, se puede desbloquear en cualquier momento de su existencia y se puede transferir la propiedad del bloqueo de una instancia a otra.Por lo tanto, siempre usa
lock_guard
, a menos que necesite las capacidades deunique_lock
. Acondition_variable
necesita aunique_lock
.fuente
A condition_variable needs a unique_lock.
- Sí, pero solo por elwait()
lado ing, como se explica en mi comentario a inf.Úselo a
lock_guard
menos que necesite poderunlock
activar manualmente el mutex sin destruir ellock
.En particular,
condition_variable
desbloquea su exclusión mutua cuando se va a dormir a las llamadas await
. Es por eso que alock_guard
no es suficiente aquí.fuente
lock_guard
y desbloquearlo, rompiendo así temporalmente la clase invariante de la guardia. Aunque esto sucede invisible para el usuario, consideraría que es una razón legítima para no permitir el usolock_guard
en este caso.lock_guard
que no permite recuperar el mutex subyacente en absoluto. Esta es una limitación deliberada para permitir un razonamiento más simple sobre el código que usalock_guard
en lugar del código que usa aunique_lock
. La única forma de lograr lo que pides es rompiendo deliberadamente la encapsulación de lalock_guard
clase y exponiendo su implementación a una clase diferente (en este caso, lacondition_variable
). Este es un precio difícil de pagar por la ventaja cuestionable del usuario de una variable de condición que no tiene que recordar la diferencia entre los dos tipos de bloqueo.condition_variable_any.wait
funcionaría con unlock_guard
? El estándar requiere que el tipo de bloqueo provisto cumpla con elBasicLockable
requisito (§30.5.2), lo cuallock_guard
no lo hace. Solo su mutex subyacente lo hace, pero por razones que señalé anteriormente, la interfaz delock_guard
no proporciona acceso al mutex.Hay ciertas cosas comunes entre
lock_guard
yunique_lock
y ciertas diferencias.Pero en el contexto de la pregunta formulada, el compilador no permite el uso de una
lock_guard
combinación en combinación con una variable de condición, porque cuando un subproceso llama a esperar en una variable de condición, el mutex se desbloquea automáticamente y cuando otros subprocesos / subprocesos notifican y el subproceso actual se invoca (sale de la espera), el bloqueo se vuelve a adquirir.Este fenómeno es contrario al principio de
lock_guard
.lock_guard
puede construirse solo una vez y destruirse solo una vez.Por
lock_guard
lo tanto , no se puede usar en combinación con una variable de condición, pero síunique_lock
se puede (porqueunique_lock
se puede bloquear y desbloquear varias veces).fuente
he compiler does not allow using a lock_guard in combination with a condition variable
Esto es falso Ciertamente no permitir y funcionan a la perfección con unalock_guard
en elnotify()
lado ing. Solo elwait()
lado int requiere ununique_lock
, porquewait()
debe liberar el bloqueo mientras se verifica la condición.En realidad no son los mismos mutexes,
lock_guard<muType>
tiene casi lo mismo questd::mutex
, con la diferencia de que su vida útil termina al final del alcance (llamado D-tor), por lo que una definición clara sobre estos dos mutexes:Y
Aquí hay un ejemplo de implementación:
En este ejemplo, usé el
unique_lock<muType>
concondition variable
fuente
Como se ha mencionado por otros, std :: unique_lock rastrea el estado bloqueado del mutex, por lo que puede diferir el bloqueo hasta después de la construcción del bloqueo y desbloquearlo antes de la destrucción del bloqueo. std :: lock_guard no permite esto.
Parece que no hay ninguna razón por la cual las funciones de espera std :: condition_variable no deberían tomar un lock_guard y un unique_lock, porque cada vez que finaliza una espera (por cualquier razón), el mutex se vuelve a adquirir automáticamente para que no cause ninguna violación semántica. Sin embargo, según el estándar, para usar std :: lock_guard con una variable de condición, debe usar std :: condition_variable_any en lugar de std :: condition_variable.
Editar : eliminado "El uso de la interfaz pthreads std :: condition_variable y std :: condition_variable_any debería ser idéntico". Al observar la implementación de gcc:
fuente