A diferença é que você pode bloquear e desbloquear a std::unique_lock
. std::lock_guard
será bloqueado apenas uma vez na construção e desbloqueado na destruição.
Portanto, para o caso de uso B, você definitivamente precisa de um std::unique_lock
para a variável de condição. No caso A, depende se você precisa trancar a proteção novamente.
std::unique_lock
possui outros recursos que permitem, por exemplo: ser construído sem bloquear o mutex imediatamente, mas criar o wrapper RAII (veja aqui ).
std::lock_guard
também fornece um invólucro RAII conveniente, mas não pode bloquear vários mutexes com segurança. Pode ser usado quando você precisar de um wrapper para um escopo limitado, por exemplo: uma função de membro:
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 esclarecer uma pergunta por chmike, por padrão std::lock_guard
e std::unique_lock
são os mesmos. Portanto, no caso acima, você pode substituir std::lock_guard
por std::unique_lock
. No entanto, std::unique_lock
pode ter um pouco mais de sobrecarga.
Note que hoje em dia deve-se usar em std::scoped_lock
vez de std::lock_guard
.
std::lock_guard
for suficiente para o seu caso A, você deve usá-lo. Não apenas evita sobrecargas desnecessárias, mas também mostra a intenção do leitor de que você nunca desbloqueará esta proteção.unique_lock
provavelmente será reduzida pelo custo de realmente bloquear e desbloquear o mutex (se o compilador não otimizar essa sobrecarga, o que seria possível).So for usecase B you definitely need a std::unique_lock for the condition variable
- sim, mas apenas no segmento quecv.wait()
é, porque esse método libera atomicamente o mutex. No outro segmento em que você atualiza a (s) variável (s) compartilhada (s) e depois chamacv.notify_one()
,lock_guard
basta um simples bloqueio do mutex no escopo ... a menos que você esteja fazendo algo mais elaborado que não consigo imaginar! por exemplo, en.cppreference.com/w/cpp/thread/condition_variable - funciona para mim :)lock_guard
eunique_lock
são praticamente a mesma coisa;lock_guard
é uma versão restrita com uma interface limitada.A
lock_guard
sempre mantém uma trava de sua construção até sua destruição. Aunique_lock
pode ser criado sem o bloqueio imediato, pode ser desbloqueado a qualquer momento da existência e pode transferir a propriedade do bloqueio de uma instância para outra.Então você sempre usa
lock_guard
, a menos que precise dos recursos dounique_lock
. Acondition_variable
precisa de umunique_lock
.fonte
A condition_variable needs a unique_lock.
- sim, mas apenas nowait()
lado ing, conforme elaborado no meu comentário ao inf.Use, a
lock_guard
menos que você precise ser capaz deunlock
alterar manualmente o mutex sem destruir olock
.Em particular,
condition_variable
desbloqueia seu mutex ao dormir após chamadas parawait
. É por isso que alock_guard
não é suficiente aqui.fonte
lock_guard
e desbloqueá-lo, interrompendo temporariamente a classe invariável do guarda. Mesmo que isso ocorra de forma invisível para o usuário, consideraria uma razão legítima para não permitir o usolock_guard
neste caso.lock_guard
que não permite recuperar o mutex subjacente. Essa é uma limitação deliberada para permitir um raciocínio mais simples sobre o código que usa,lock_guard
em oposição ao código que usa aunique_lock
. A única maneira de conseguir o que você pede é quebrar deliberadamente o encapsulamento dalock_guard
classe e expor sua implementação a uma classe diferente (neste caso, ocondition_variable
). Esse é um preço difícil de pagar pela vantagem questionável do usuário de uma variável de condição, sem precisar lembrar a diferença entre os dois tipos de bloqueio.condition_variable_any.wait
funcionaria com umlock_guard
? A norma exige que o tipo de bloqueio fornecido atenda aoBasicLockable
requisito (§30.5.2), o quelock_guard
não ocorre. Somente o mutex subjacente fornece, mas, por razões que apontei anteriormente, a interface dolock_guard
não fornece acesso ao mutex.Existem certas coisas comuns entre
lock_guard
eunique_lock
e certas diferenças.Mas, no contexto da pergunta, o compilador não permite o uso de uma
lock_guard
combinação com uma variável de condição, porque quando um thread chama a espera de uma variável de condição, o mutex é desbloqueado automaticamente e quando outros threads / threads notificam e o segmento atual é invocado (sai da espera), o bloqueio é adquirido novamente.Este fenômeno é contra o princípio de
lock_guard
.lock_guard
pode ser construído apenas uma vez e destruído apenas uma vez.Portanto,
lock_guard
não pode ser usado em combinação com uma variável de condição, masunique_lock
pode ser (porqueunique_lock
pode ser bloqueado e desbloqueado várias vezes).fonte
he compiler does not allow using a lock_guard in combination with a condition variable
Isto é falso. Certamente não permitir e trabalho perfeitamente com umlock_guard
nonotify()
lado ing. Somente owait()
lado int requer aunique_lock
, porquewait()
deve liberar o bloqueio enquanto verifica a condição.Eles não são realmente os mesmos mutexes,
lock_guard<muType>
tem quase o mesmo questd::mutex
, com uma diferença de que a vida útil termina no final do escopo (chamado D-tor), portanto, uma definição clara sobre esses dois mutexes:E
Aqui está um exemplo de implementação:
Neste exemplo, eu usei o
unique_lock<muType>
comcondition variable
fonte
Como já foi mencionado por outros, std :: unique_lock rastreia o status de bloqueio do mutex, para que você possa adiar o bloqueio até depois da construção do bloqueio e desbloqueá-lo antes da destruição do bloqueio. std :: lock_guard não permite isso.
Parece não haver razão para as funções de espera std :: condition_variable não receberem um lock_guard, assim como um unique_lock, porque sempre que uma espera termina (por qualquer motivo), o mutex é readquirido automaticamente, de modo a não causar nenhuma violação semântica. No entanto, de acordo com o padrão, para usar um std :: lock_guard com uma variável de condição, é necessário usar um std :: condition_variable_any em vez de std :: condition_variable.
Edit : delete "Usando a interface pthreads, std :: condition_variable e std :: condition_variable_any devem ser idênticos". Ao analisar a implementação do gcc:
fonte