O C ++ 17 introduziu uma nova classe de bloqueio chamada std::scoped_lock
.
A julgar pela documentação, ela se parece com a std::lock_guard
classe já existente .
Qual é a diferença e quando devo usá-la?
O C ++ 17 introduziu uma nova classe de bloqueio chamada std::scoped_lock
.
A julgar pela documentação, ela se parece com a std::lock_guard
classe já existente .
Qual é a diferença e quando devo usá-la?
Respostas:
A scoped_lock
é uma versão estritamente superior lock_guard
que bloqueia um número arbitrário de semáforos todos de uma só vez (com o mesmo algoritmo para evitar impasse como std::lock
). No novo código, você deve sempre usar scoped_lock
.
O único motivo que lock_guard
ainda existe é a compatibilidade. Não pôde ser excluído apenas porque é usado no código atual. Além disso, mostrou-se indesejável alterar sua definição (de unária para variável), porque essa também é uma mudança observável e, portanto, quebradora (mas por razões um tanto técnicas).
lock_guard
. Mas certamente torna as classes de guarda um pouco mais fáceis de usar.
A diferença única e importante é que std::scoped_lock
um construtor variadico recebe mais de um mutex. Isso permite bloquear vários mutexes em um impasse, evitando como se std::lock
fossem usados.
{
// safely locked as if using std::lock
std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);
}
Anteriormente, você tinha que dançar um pouco para bloquear vários mutexes de uma maneira segura, usando a std::lock
explicação desta resposta .
A adição do bloqueio de escopo facilita o uso e evita os erros relacionados. Você pode considerar std::lock_guard
obsoleto. O caso de argumento único de std::scoped_lock
pode ser implementado como uma especialização e você não precisa se preocupar com possíveis problemas de desempenho.
O GCC 7 já tem suporte para o std::scoped_lock
qual pode ser visto aqui .
Para obter mais informações, leia o documento padrão.
scoped_lock lk; // locks all mutexes in scope
. LGTM.
scoped_lock lk;
é o novo atalho para scoped_lock<> lk;
. Não são há semáforos. Então você está certo. ;-)
Resposta tardia e principalmente em resposta a:
Você pode considerar
std::lock_guard
obsoleto.
No caso comum de que é necessário bloquear exatamente um mutex, std::lock_guard
existe uma API um pouco mais segura de usar do que scoped_lock
.
Por exemplo:
{
std::scoped_lock lock; // protect this block
...
}
O trecho de código acima provavelmente é um erro acidental em tempo de execução porque ele compila e, em seguida, não faz absolutamente nada. O codificador provavelmente significava:
{
std::scoped_lock lock{mut}; // protect this block
...
}
Agora ele bloqueia / desbloqueia mut
.
Se lock_guard
foi usado nos dois exemplos acima, o primeiro exemplo é um erro em tempo de compilação em vez de um erro em tempo de execução, e o segundo exemplo tem funcionalidade idêntica à versão usada scoped_lock
.
Portanto, meu conselho é usar a ferramenta mais simples para o trabalho:
lock_guard
se você precisar bloquear exatamente 1 mutex para um escopo inteiro.
scoped_lock
se você precisar bloquear um número de mutexes que não sejam exatamente 1.
unique_lock
se você precisar desbloquear dentro do escopo do bloco (que inclui o uso de a condition_variable
).
Este conselho é que não implica que scoped_lock
deve ser redesenhado para não aceitar 0 semáforos. Existem casos de uso válidos nos quais é desejável scoped_lock
aceitar pacotes de parâmetros de modelos variados que possam estar vazios. E a caixa vazia não deve bloquear nada.
E é por isso que lock_guard
não é preterido. scoped_lock
e unique_lock
pode ser um superconjunto de funcionalidades de lock_guard
, mas esse fato é uma faca de dois gumes. Às vezes, é tão importante o que um tipo não fará (construção padrão neste caso).
Aqui está um exemplo e uma citação de C ++ Concurrency in Action :
friend void swap(X& lhs, X& rhs)
{
if (&lhs == & rhs)
return;
std::lock(lhs.m, rhs.m);
std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
swap(lhs.some_detail, rhs.some_detail);
}
vs.
friend void swap(X& lhs, X& rhs)
{
if (&lhs == &rhs)
return;
std::scoped_lock guard(lhs.m, rhs.m);
swap(lhs.some_detail, rhs.some_detail);
}
A existência de
std::scoped_lock
significa que a maioria dos casos em que você usariastd::lock
antes do c ++ 17 agora pode ser escrita usandostd::scoped_lock
, com menos potencial para erros, o que só pode ser uma coisa boa!