web-dev-qa-db-fra.com

std :: unique_lock <std :: mutex> ou std :: lock_guard <std :: mutex>?

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?

307
chmike

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_.

299
inf

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.

102
Sebastian Redl

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.

43
ComicSansMS

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).

9
Sandeep