C ++ 17 a introduit une nouvelle classe de verrouillage appelée std::scoped_lock
.
À en juger par la documentation, il ressemble à la std::lock_guard
classe déjà existante .
Quelle est la différence et quand dois-je l'utiliser?
C ++ 17 a introduit une nouvelle classe de verrouillage appelée std::scoped_lock
.
À en juger par la documentation, il ressemble à la std::lock_guard
classe déjà existante .
Quelle est la différence et quand dois-je l'utiliser?
Réponses:
Il scoped_lock
s'agit d'une version strictement supérieure de lock_guard
qui verrouille un nombre arbitraire de mutex en même temps (en utilisant le même algorithme d'évitement de blocage que std::lock
). Dans le nouveau code, vous ne devriez jamais utiliser scoped_lock
.
La seule raison lock_guard
existe encore est la compatibilité. Il ne pouvait pas simplement être supprimé, car il est utilisé dans le code actuel. De plus, il s'est avéré indésirable de changer sa définition (de unaire à variadique), car c'est aussi un changement observable, et donc cassant (mais pour des raisons quelque peu techniques).
lock_guard
. Mais cela rend certainement les classes de garde un peu plus faciles à utiliser.
La seule et importante différence est qu'un std::scoped_lock
constructeur variadique prend plus d'un mutex. Cela permet de verrouiller plusieurs mutex de manière à éviter les interblocages comme s'ils std::lock
étaient utilisés.
{
// safely locked as if using std::lock
std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);
}
Auparavant, vous deviez faire une petite danse pour verrouiller plusieurs mutex de manière sûre en utilisant std::lock
comme expliqué cette réponse .
L'ajout du verrouillage de la portée facilite l'utilisation et évite les erreurs associées. Vous pouvez considérer comme std::lock_guard
obsolète. Le cas d'argument unique de std::scoped_lock
peut être implémenté en tant que spécialisation et vous n'avez donc pas à craindre d'éventuels problèmes de performances.
GCC 7 a déjà un support pour std::scoped_lock
lequel peut être vu ici .
Pour plus d'informations, vous voudrez peut-être lire l' article standard
scoped_lock lk; // locks all mutexes in scope
. LGTM.
scoped_lock lk;
est le nouveau raccourci pour scoped_lock<> lk;
. Il n'y a pas de mutex. Alors tu as raison. ;-)
Réponse tardive, et principalement en réponse à:
Vous pouvez considérer comme
std::lock_guard
obsolète.
Pour le cas courant où l'on a besoin de verrouiller exactement un mutex, std::lock_guard
dispose d'une API qui est un peu plus sûre à utiliser que scoped_lock
.
Par exemple:
{
std::scoped_lock lock; // protect this block
...
}
L'extrait ci-dessus est probablement une erreur d'exécution accidentelle car il se compile et ne fait absolument rien. Le codeur voulait probablement dire:
{
std::scoped_lock lock{mut}; // protect this block
...
}
Maintenant, il se verrouille / se déverrouille mut
.
Si a lock_guard
été utilisé dans les deux exemples ci-dessus à la place, le premier exemple est une erreur de compilation au lieu d'une erreur d'exécution, et le deuxième exemple a des fonctionnalités identiques à la version qui utilise scoped_lock
.
Mon conseil est donc d'utiliser l'outil le plus simple pour le travail:
lock_guard
si vous avez besoin de verrouiller exactement 1 mutex pour une étendue entière.
scoped_lock
si vous avez besoin de verrouiller un nombre de mutex différent de 1.
unique_lock
si vous avez besoin de déverrouiller dans le cadre du bloc (qui comprend l'utilisation avec a condition_variable
).
Ce conseil n'implique pas qu'il scoped_lock
devrait être repensé pour ne pas accepter 0 mutex. Il existe des cas d'utilisation valides où il est souhaitable scoped_lock
d'accepter des packs de paramètres de modèles variadiques qui peuvent être vides. Et la valise vide ne doit rien verrouiller.
Et c'est pourquoi lock_guard
n'est pas obsolète. scoped_lock
et unique_lock
peut être un sur-ensemble de fonctionnalités de lock_guard
, mais ce fait est une arme à double tranchant. Parfois, ce qu'un type ne fera pas est tout aussi important (construction par défaut dans ce cas).
Voici un exemple et une citation de C ++ Concurrency en 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);
}
contre.
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);
}
L'existence de
std::scoped_lock
signifie que la plupart des cas où vous auriez utiliséstd::lock
avant c ++ 17 peuvent maintenant être écrits en utilisantstd::scoped_lock
, avec moins de risques d'erreurs, ce qui ne peut être qu'une bonne chose!