std::unique_lock<std::mutex> o std::lock_guard<std::mutex>?
Tengo dos casos de uso.
R. Quiero sincronizar el acceso a una cola para dos subprocesos.
B. Quiero sincronizar el acceso a una cola para dos subprocesos y usar una variable de condición porque uno de los subprocesos esperará a que el otro subproceso almacene el contenido en la cola.
Para el caso de uso AI, consulte el ejemplo de código usando std::lock_guard<>
. Para el caso de uso de BI, consulte el ejemplo de código usando std::unique_lock<>
.
¿Cuál es la diferencia entre los dos y cuál debo usar en cada caso de uso?
La diferencia es que puedes bloquear y desbloquear un archivo std::unique_lock
. std::lock_guard
se bloqueará solo una vez en la construcción y se desbloqueará en la destrucción.
Entonces, para el caso de uso B, definitivamente necesitas a std::unique_lock
para la variable de condición. En el caso A, depende de si es necesario volver a bloquear el protector.
std::unique_lock
tiene otras características que le permiten, por ejemplo: construirse sin bloquear el mutex inmediatamente sino construir el contenedor RAII (ver aquí ).
std::lock_guard
también proporciona un contenedor RAII conveniente, pero no puede bloquear múltiples mutex de forma 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 duda por chmike, por defecto std::lock_guard
y std::unique_lock
son lo mismo. Entonces, en el caso anterior, podrías reemplazarlo 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 (desde C++ 17) se debe usar std::scoped_lock
en lugar de std::lock_guard
.
lock_guard
y unique_lock
son prácticamente lo mismo; lock_guard
Es una versión restringida con una interfaz limitada.
A lock_guard
siempre mantiene un candado desde su construcción hasta su destrucción. Se unique_lock
puede crear sin bloquearlo inmediatamente, desbloquearlo en cualquier momento de su existencia y transferir la propiedad del bloqueo de una instancia a otra.
Por eso siempre usas lock_guard
, a menos que necesites las capacidades de unique_lock
. A condition_variable
necesita un unique_lock
.
Úselo lock_guard
a menos que necesite poder establecer manualmente unlock
el mutex en el medio sin destruir el archivo lock
.
En particular, condition_variable
desbloquea su mutex cuando se va a dormir al recibir llamadas a wait
. Por eso lock_guard
aquí a no es suficiente.
Si ya utiliza C++ 17 o posterior, considere utilizar scoped_lock
una versión ligeramente mejorada de lock_guard
, con las mismas capacidades esenciales.
Hay ciertas cosas comunes entre lock_guard
y unique_lock
y ciertas diferencias.
Pero en el contexto de la pregunta formulada, el compilador no permite el uso lock_guard
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 notifican y el subproceso actual se invoca (sale de la espera), se vuelve a adquirir el bloqueo.
Este fenómeno va en contra del principio de lock_guard
. lock_guard
Sólo puede construirse una vez y destruirse sólo una vez.
Por lo tanto, lock_guard
no se puede usar en combinación con una variable de condición, pero sí unique_lock
(porque unique_lock
se puede bloquear y desbloquear varias veces).
Una diferencia que falta es:
std::unique_lock
se puede mover pero std::lock_guard
no se puede mover.
Nota: Ambos no se pueden copiar.