79 votes

Un objet verrouillé reste-t-il verrouillé si une exception se produit à l'intérieur?

Dans l'application de filetage #, si je verrouille un objet, imaginons une file d'attente, et si une exception se produit, l'objet restera-t-il verrouillé? Voici le pseudo-code:

 int ii;
lock(MyQueue)
{
   MyClass LclClass = (MyClass)MyQueue.Dequeue();
   try
   {
      ii = int.parse(LclClass.SomeString);
   }
   catch
   {
     MessageBox.Show("Error parsing string");
   }
}
 

Si je comprends bien, le code après la capture ne s’exécute pas, mais je me demandais si le verrou serait libéré.

95voto

Eric Lippert Points 300275

Je remarque que personne n'a mentionné dans leurs réponses à cette question aussi ancienne que la libération d'un verrou sur une exception est incroyablement dangereux. Oui, verrouillage de déclarations en C# ont "enfin" sémantique; lors de la commande quitte la serrure normalement ou anormalement, le verrou est libéré. Vous êtes tous de parler de ce que c'est une bonne chose, mais c'est une mauvaise chose! La bonne chose à faire si vous avez une région verrouillée, qui lève une exception non gérée est de mettre fin à l'malades processus immédiatement avant qu'il ne détruit plus les données de l'utilisateur, de ne pas libérer le verrou et continuer à avancer.

Regardons cela de cette façon: supposons que vous disposez d'une salle de bains avec un verrou sur la porte et une ligne de gens qui attendent à l'extérieur. Une bombe dans la salle de bain s'en va, à tuer la personne. Votre question est: "dans cette situation, le verrou est automatiquement déverrouillé de sorte que la prochaine personne peut entrer dans la salle de bain?" Oui, il le fera. Ce n'est pas une bonne chose. Une bombe vient de sonner et il les a tué quelqu'un! La plomberie est probablement détruit, la maison n'est plus une structure solide, et il y a peut être une autre bombe là. La bonne chose à faire est de mettre tout le monde aussi vite que possible et de démolir l'ensemble de la maison.

Je veux dire, pensez-y: si vous verrouillez une région de code pour lire à partir d'une structure de données sans qu'il soit muté sur un autre thread, et quelque chose dans cette structure de données a déclenché une exception, les chances sont bonnes que c'est parce que la structure de données est corrompue. Les données de l'utilisateur est maintenant foiré; vous ne voulez pas essayer de sauver les données de l'utilisateur à ce point, parce que vous êtes alors économiser de corrompre les données. Juste de terminer le processus.

Si vous verrouillez une région de code pour effectuer une mutation sans un autre thread de lecture de l'état dans le même temps, et la mutation lancers, alors si les données n'a pas été endommagé avant, c'est sûr qu'il est maintenant. Ce qui est exactement le scénario que la serrure est censée protéger contre. Maintenant, le code qui est dans l'attente de lire que l'état aura immédiatement accès à la corruption de l'état, et sans doute lui-même plantage. Encore une fois, la bonne chose à faire est de mettre fin au processus.

Peu importe comment vous le trancher, une exception à l'intérieur d'une serrure est une mauvaise nouvelle. La bonne question à se poser n'est pas "ma serrure être nettoyé dans le cas d'une exception?" La bonne question à se poser est "comment puis-je m'assurer qu'il n'y est jamais une exception à l'intérieur d'une serrure? Et si il y est, alors comment puis-je organiser mon programme de sorte que les mutations sont annulées à la précédente dans un bon état?"

85voto

Marc Gravell Points 482669

En premier; avez-vous envisagé de TryParse?

in li;
if(int.TryParse(LclClass.SomeString, out li)) {
    // li is now assigned
} else {
    // input string is dodgy
}

Le verrou est libéré pour 2 raisons; la première, lock essentiellement:

Monitor.Enter(lockObj);
try {
  // ...
} finally {
    Monitor.Exit(lockObj);
}

Seconde; vous rattraper et ne pas re-jeter l'exception interne, de sorte que le lock jamais voit réellement une exception. Bien sûr, vous êtes maintenant le verrou pour la durée d'une MessageBox, qui pourrait être un problème.

Il sera donc publié une tous les, mais le plus mortel catastrophique irrécupérable exceptions.

42voto

Marc Gravell Points 482669

oui, qui va libérer correctement; lock des actes en tant que try/finally, avec l' Monitor.Exit(myLock) dans le enfin, donc, peu importe comment vous quitter, il sera libéré. Comme une note, catch(... e) {throw e;} est préférable d'éviter, tant que les dommages à l'stack-trace sur l' e; il est préférable de ne pas l'attraper à tous, ou alternativement: utiliser throw; plutôt que d' throw e; qui ne un lever de nouveau.

Si vous voulez vraiment le savoir, un verrou en C#4 / .NET 4 est:

{
    bool haveLock = false;
    try {
       Monitor.Enter(myLock, ref haveLock);
    } finally {
       if(haveLock) Monitor.Exit(myLock);
    }
} 

14voto

GH. Points 91

"Une instruction de verrouillage est compilée pour un appel à Monitor.Enter, puis un bloc try… finally. Dans le bloc finally, Monitor.Exit est appelé.

La génération de code JIT pour x86 et x64 garantit qu’un arrêt de thread ne peut pas se produire entre un appel Monitor.Enter et un bloc try qui le suit immédiatement. "

Tiré de: Ce site

5voto

Brian Rasmussen Points 68853

Juste pour ajouter un peu de Marc est une excellente réponse.

Des Situations de ce genre sont la raison même de l'existence de l' lock mot-clé. Il permet aux développeurs assurez-vous que le verrou est libéré dans l' finally bloc.

Si vous êtes obligés d'utiliser Monitor.Enter/Exit par exemple pour soutenir un délai d'attente, vous devez assurez-vous de placer à l'appel d' Monitor.Exit dans la finally bloc pour assurer une bonne libération de la serrure dans le cas d'une exception.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X