std::unique_lock<std::mutex> o std::lock_guard<std::mutex>?

Resuelto chmike asked hace 10 años • 7 respuestas

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?

chmike avatar Dec 11 '13 17:12 chmike
Aceptado

La diferencia es que puedes bloquear y desbloquear un archivo std::unique_lock. std::lock_guardse 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_lockpara la variable de condición. En el caso A, depende de si es necesario volver a bloquear el protector.

std::unique_locktiene otras características que le permiten, por ejemplo: construirse sin bloquear el mutex inmediatamente sino construir el contenedor RAII (ver aquí ).

std::lock_guardtambié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_guardy std::unique_lockson lo mismo. Entonces, en el caso anterior, podrías reemplazarlo std::lock_guardcon std::unique_lock. Sin embargo, std::unique_lockpodrí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_locken lugar de std::lock_guard.

Stephan Dollberg avatar Dec 11 '2013 10:12 Stephan Dollberg

lock_guardy unique_lockson prácticamente lo mismo; lock_guardEs una versión restringida con una interfaz limitada.

A lock_guardsiempre mantiene un candado desde su construcción hasta su destrucción. Se unique_lockpuede 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_variablenecesita un unique_lock.

Sebastian Redl avatar Dec 11 '2013 10:12 Sebastian Redl

Úselo lock_guarda menos que necesite poder establecer manualmente unlockel mutex en el medio sin destruir el archivo lock.

En particular, condition_variabledesbloquea su mutex cuando se va a dormir al recibir llamadas a wait. Por eso lock_guardaquí a no es suficiente.

Si ya utiliza C++ 17 o posterior, considere utilizar scoped_lockuna versión ligeramente mejorada de lock_guard, con las mismas capacidades esenciales.

ComicSansMS avatar Dec 11 '2013 10:12 ComicSansMS

Hay ciertas cosas comunes entre lock_guardy unique_locky ciertas diferencias.

Pero en el contexto de la pregunta formulada, el compilador no permite el uso lock_guarden 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_guardSólo puede construirse una vez y destruirse sólo una vez.

Por lo tanto, lock_guardno se puede usar en combinación con una variable de condición, pero sí unique_lock(porque unique_lockse puede bloquear y desbloquear varias veces).

Sandeep avatar Sep 16 '2014 12:09 Sandeep

Una diferencia que falta es: std::unique_lockse puede mover pero std::lock_guardno se puede mover.

Nota: Ambos no se pueden copiar.

user3500315 avatar Sep 06 '2020 10:09 user3500315