Verrous rentrants en C #

119

Le code suivant entraînera-t-il un blocage à l'aide de C # sur .NET?

 class MyClass
 {
    private object lockObj = new object();

    public void Foo()
    {
        lock(lockObj)
        { 
             Bar();
        }
    }

    public void Bar()
    {
        lock(lockObj)
        { 
          // Do something 
        }
    }       
 }
Gars
la source
6
Pouvons-nous envisager de changer le titre de cette question - peut-être en quelque chose comme le récemment fermé Pourquoi les verrous imbriqués ne provoquent-ils pas une impasse? En l'état, le titre semble presque conçu pour empêcher les gens de le découvrir.
Jeff Sternal
12
En fait, j'ai trouvé cela basé sur le mot de recherche «réentrant», et cela a répondu à ma question. Si c'est une question dup, c'est un problème différent ...
emfurry
Je suis d'accord avec @JeffSternal comment cette question suppose que la personne qui recherche la question est déjà familière avec les verrous "Re-entrant". Une autre question de duplication, je pense, avait un bon titre pour cela: stackoverflow.com/questions/3687505/...
Luis Perez

Réponses:

148

Non, pas tant que vous vous verrouillez sur le même objet. Le code récursif a effectivement déjà le verrou et peut donc continuer sans entrave.

lock(object) {...}est un raccourci pour utiliser la classe Monitor . Comme Marc le souligne , Monitorautorise la rentrée , donc des tentatives répétées de verrouillage sur un objet sur lequel le thread actuel a déjà un verrou fonctionneront très bien.

Si vous commencez à verrouiller différents objets, c'est à ce moment-là que vous devez faire attention. Portez une attention particulière à:

  • Acquérez toujours des verrous sur un nombre donné d'objets dans la même séquence.
  • Relâchez toujours les verrous dans l' ordre inverse de la façon dont vous les acquérez.

Si vous enfreignez l'une de ces règles, vous êtes pratiquement assuré d'avoir des problèmes de blocage à un moment donné .

Voici une bonne page Web décrivant la synchronisation des threads dans .NET: http://dotnetdebug.net/2005/07/20/monitor-class-avoiding-deadlocks/

Verrouillez également le moins d'objets possible à la fois. Pensez à appliquer des verrous à gros grains lorsque cela est possible. L'idée étant que si vous pouvez écrire votre code de manière à ce qu'il y ait un graphe d'objets et que vous puissiez acquérir des verrous à la racine de ce graphe d'objets, alors faites-le. Cela signifie que vous avez un verrou sur cet objet racine et que vous n'avez donc pas à vous soucier autant de la séquence dans laquelle vous acquérez / libérez les verrous.

(Une remarque supplémentaire, votre exemple n'est pas techniquement récursif. Pour qu'il soit récursif, il Bar()devrait s'appeler lui-même, généralement dans le cadre d'une itération.)

Neil Barnwell
la source
1
Surtout dans différentes séquences.
Marc Gravell
6
Re récursivité; En effet; pour le bénéfice de Guy, le terme est rentrant
Marc Gravell
Merci pour la clarification sur la terminologie - j'ai édité et corrigé la question.
Guy
Cette question semble attirer beaucoup d'attention, j'ai donc mis à jour ma réponse avec quelques autres notes que j'ai trouvées depuis que je l'ai écrite pour la première fois.
Neil Barnwell
En fait, je ne pense pas que l'ordre dans lequel vous libérez les verrous importe. L'ordre dans lequel vous les choisissez le fait certainement, mais tant que la libération du verrou n'est conditionnée à rien (vous pouvez le libérer à tout moment), tout devrait bien se passer tant que vous libérez tous les verrous que vous avez acquis.
bobroxsox
20

Eh bien, Monitorpermet la rentrée, donc vous ne pouvez pas vous bloquer ... donc non: cela ne devrait pas faire

Marc Gravell
la source
7

Si un thread détient déjà un verrou, il ne se bloquera pas. Le framework .Net garantit cela. Vous devez seulement vous assurer que deux threads n'essaient pas d'acquérir les mêmes deux verrous dans le désordre par les chemins de code.

Le même thread peut acquérir le même verrou plusieurs fois, mais vous devez vous assurer de libérer le verrou le même nombre de fois que vous l'avez acquis. Bien sûr, tant que vous utilisez le mot-clé "lock" pour ce faire, cela se produit automatiquement.

Jeffrey L Whitledge
la source
Notez que c'est vrai pour les moniteurs, mais pas nécessairement pour les autres types de verrouillage.
Jon Skeet
(Ne souhaitant pas laisser entendre que vous ne saviez pas cela, bien sûr - juste que c'est une distinction importante :)
Jon Skeet
C'est un bon point. J'allais en fait changer "verrouiller" en "surveiller" tout au long, mais ensuite je me suis distrait. Et paresseux. Et le comportement est également vrai pour les objets Windows mutex kernal, alors j'ai pensé, assez proche!
Jeffrey L Whitledge
5

Non, ce code n'aura pas de verrous morts. Si vous voulez vraiment créer un blocage le plus simple, il faut au moins 2 ressources. Considérez le scénario du chien et des os. 1. Un chien a un contrôle total sur 1 os, donc tout autre chien doit attendre. 2. 2 chiens avec 2 os sont au minimum requis pour créer une impasse lorsqu'ils verrouillent leurs os respectivement et recherchent également d'autres os.

.. et ainsi de suite n chiens et m os et provoquent des blocages plus sophistiqués.

Rishabh Jain
la source