J'ai deux cas d'utilisation.
A. Je veux synchroniser l'accès de deux threads à une file d'attente.
B. Je souhaite synchroniser l'accès de deux threads à une file d'attente et utiliser une variable de condition, car l'un des threads attendra que le contenu soit stocké dans la file d'attente par l'autre thread.
Pour le cas d'utilisation A, je vois un exemple de code utilisant std::lock_guard<>
. Pour le cas d'utilisation B, je vois un exemple de code utilisant std::unique_lock<>
.
Quelle est la différence entre les deux et lequel devrais-je utiliser dans quel cas d'utilisation?
La différence est que vous pouvez verrouiller et déverrouiller un _std::unique_lock
_. _std::lock_guard
_ sera verrouillé une seule fois lors de la construction et déverrouillé lors de la destruction.
Donc, pour le cas d'utilisation B, vous avez certainement besoin d'un _std::unique_lock
_ pour la variable de condition. Dans le cas A cela dépend si vous devez relocaliser la garde.
_std::unique_lock
_ possède d’autres fonctionnalités qui lui permettent, par exemple: d’être construites sans verrouiller immédiatement le mutex mais de construire l’enveloppe RAII (voir ici ).
_std::lock_guard
_ fournit également un wrapper RAII pratique, mais ne peut pas verrouiller plusieurs mutex en toute sécurité. Il peut être utilisé lorsque vous avez besoin d'un wrapper pour une portée limitée, par exemple une fonction membre:
_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
};
_
Pour clarifier une question posée par chmike, par défaut, _std::lock_guard
_ et _std::unique_lock
_ sont identiques. Ainsi, dans le cas ci-dessus, vous pouvez remplacer _std::lock_guard
_ par _std::unique_lock
_. Cependant, _std::unique_lock
_ pourrait avoir un peu plus de temps système.
Notez que ces jours-ci, on devrait utiliser std::scoped_lock
au lieu de _std::lock_guard
_.
lock_guard
et unique_lock
sont à peu près la même chose; lock_guard
est une version restreinte avec une interface limitée.
Un lock_guard
détient toujours un verrou de sa construction à sa destruction. Un unique_lock
peut être créé sans verrouillage immédiat, peut être déverrouillé à tout moment de son existence et peut transférer la propriété du verrou d'une instance à une autre.
Donc, vous utilisez toujours lock_guard
, sauf si vous avez besoin des fonctionnalités de unique_lock
. Un condition_variable
a besoin d'un unique_lock
.
Utilisez lock_guard
sauf si vous avez besoin de pouvoir manuellement unlock
le mutex entre les deux sans détruire le lock
.
En particulier, condition_variable
déverrouille son mutex lorsqu'il s'endort lors d'un appel à wait
. C'est pourquoi un lock_guard
ne suffit pas ici.
Il y a certaines choses communes entre lock_guard
et unique_lock
et certaines différences.
Mais dans le contexte de la question posée, le compilateur n'autorise pas l'utilisation d'un lock_guard
en combinaison avec une variable de condition, car lorsqu'un appel de fil attend sur une variable de condition, le mutex est déverrouillé automatiquement et lorsque d'autres threads notify et le thread actuel est appelé (sort de l'attente), le verrou est ré-acquis.
Ce phénomène va à l’encontre du principe de lock_guard
. lock_guard
ne peut être construit qu'une seule fois et détruit une seule fois.
Par conséquent, lock_guard
ne peut pas être associé à une variable de condition, mais un unique_lock
peut l'être (car unique_lock
peut être verrouillé et déverrouillé plusieurs fois).