web-dev-qa-db-fra.com

Combien de temps un verrou C # attendra-t-il et que se passe-t-il si le code se bloque pendant le verrouillage?

j'ai vu le code suivant et je voulais l'utiliser pour une activité simple qui ne peut être exécutée qu'une à la fois et ne se produira pas souvent (de sorte que le risque de se produire deux fois à la fois est très faible, mais on ne sait jamais ).

Donc le code:

 //class variable
    private static object syncRoot = new object();

    //in a method:
    lock (syncRoot)
    {
     DoIt();
    }

quand un autre thread passe et veut exécuter le code, combien de temps attendra-t-il jusqu'à ce que le verrou soit libéré? pour toujours, ou pouvez-vous en quelque sorte définir un délai d'attente?

et deuxièmement: si la méthode DoIt() lève une exception, le verrou est-il toujours relâché?

39
Michel

quand un autre thread passe et veut exécuter le code, combien de temps attendra-t-il jusqu'à ce que le verrou soit libéré?

lock bloquera le thread en essayant d'entrer dans le verrou indéfiniment jusqu'à ce que l'objet verrouillé soit libéré.

pouvez-vous en quelque sorte mouiller un temps mort?

Si vous devez spécifier un délai d’expiration, utilisez Monitor.TryEnter as dans

if(Monitor.TryEnter(obj, new TimeSpan(0, 0, 1))) {
    try {
        body 
    }
    finally {
        Monitor.Exit(obj);
    }
}

si la méthode DoIt () lève une exception, le verrou est-il toujours publié?

Oui, un lock(obj) { body } est traduit en

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }

Pour les détails sanglants sur ce qui peut arriver quand une exception est levée, voir Les verrous et les exceptions ne se mélangent pas .

68
jason

Comme mentionné, un verrou régulier attendra pour toujours, ce qui présente un risque d'impasse.

Le mécanisme préféré est (et notez la ref):

bool lockTaken = false;
try {
    Monitor.TryEnter(lockObj, timeout, ref lockTaken);
    if(!lockTaken) throw new TimeoutException(); // or compensate
    // work here...
} finally {
    if(lockTaken) Monitor.Exit(lockObj);
}

Cela évite le risque de ne pas libérer le verrou dans certains cas Edge.

La finally (qui existe dans toute implémentation sensible) garantit que le verrou est libéré même en cas d'erreur.

29
Marc Gravell

Une simple lock(syncRoot) attendra pour toujours. 

Vous pouvez le remplacer par 

if (System.Threading.Monitor.TryEnter(syncRoot, 1000))
{
     try
     {
         DoIt();
     }
     finally
     {
         System.Threading.Monitor.Exit(syncRoot);
     }
}

Chaque fil doit assurer un verrouillage exceptionnellement sûr. 

Notez que le standard lock(syncRoot) {} est réécrit en Monitor.Enter(syncRoot)

7
Henk Holterman

La serrure attendra pour toujours, comme l'a dit Henk. Une exception sera toujours déverrouillée. Il est implémenté en interne avec un bloc try-finally.

3
Mr47

Vous devriez prendre du recul et vous demander pourquoi vous laissez une exception à une méthode avoir son mot à dire quand une variable Monitorvous Enter est Exited. S'il y a même une chance que DoIt() lève une exception (et je dirais que si possible vous réécrivez DoIt() pour qu'il ne le fasse pas), alors vous devriez avoir un bloc try/catch dans l'instruction lock () pour pouvoir vous assurer tout nettoyage nécessaire.

1
dlev

Connaître les conditions préalables nécessaires à l'impasse. Toujours utiliser Monitor vs Lock pour éviter les blocages.

 Conditions

0
dot.net5000