La diferencia es que puedes bloquear y desbloquear a std::unique_lock. std::lock_guardserá bloqueado solo una vez en construcción y desbloqueado en destrucción.
Entonces, para el caso de uso B, definitivamente necesita un std::unique_lockpara la variable de condición. En el caso A, depende de si necesita volver a bloquear la guardia.
std::unique_locktiene otras características que le permiten, por ejemplo: construirse sin bloquear el mutex inmediatamente, pero construir el contenedor RAII (ver aquí ).
std::lock_guardtambié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_guardy std::unique_lockson lo mismo. Entonces, en el caso anterior, podría reemplazar 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 se debe usar en std::scoped_locklugar de std::lock_guard.
                 
                
                
                 
std::lock_guardes 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_lockes 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_guardsuficiente 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_guardyunique_lockson más o menos lo mismo;lock_guardes una versión restringida con una interfaz limitada.A
lock_guardsiempre tiene un candado desde su construcción hasta su destrucción. Aunique_lockse 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_variablenecesita 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_guardmenos que necesite poderunlockactivar manualmente el mutex sin destruir ellock.En particular,
condition_variabledesbloquea su exclusión mutua cuando se va a dormir a las llamadas await. Es por eso que alock_guardno es suficiente aquí.fuente
lock_guardy 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_guarden este caso.lock_guardque 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_guarden lugar del código que usa aunique_lock. La única forma de lograr lo que pides es rompiendo deliberadamente la encapsulación de lalock_guardclase 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.waitfuncionaría con unlock_guard? El estándar requiere que el tipo de bloqueo provisto cumpla con elBasicLockablerequisito (§30.5.2), lo cuallock_guardno lo hace. Solo su mutex subyacente lo hace, pero por razones que señalé anteriormente, la interfaz delock_guardno proporciona acceso al mutex.Hay ciertas cosas comunes entre
lock_guardyunique_locky ciertas diferencias.Pero en el contexto de la pregunta formulada, el compilador no permite el uso de una
lock_guardcombinació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_guardpuede construirse solo una vez y destruirse solo una vez.Por
lock_guardlo tanto , no se puede usar en combinación con una variable de condición, pero síunique_lockse puede (porqueunique_lockse puede bloquear y desbloquear varias veces).fuente
he compiler does not allow using a lock_guard in combination with a condition variableEsto es falso Ciertamente no permitir y funcionan a la perfección con unalock_guarden 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 variablefuente
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