Comment fonctionne le verrouillage exactement?

527

Je vois que pour utiliser des objets qui ne sont pas sûrs pour les threads, nous enveloppons le code avec un verrou comme ceci:

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

Que se passe-t-il donc lorsque plusieurs threads accèdent au même code (supposons qu'il s'exécute dans une application Web ASP.NET). Sont-ils en file d'attente? Si oui, combien de temps attendront-ils?

Quel est l'impact sur les performances en raison de l'utilisation de verrous?

NLV
la source
1
^ lien mort, voir: jonskeet.uk/csharp/threads/index.html
Ivan Pavičić

Réponses:

448

L' lockinstruction est traduite par C # 3.0 comme suit:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

En C # 4.0, cela a changé et il est maintenant généré comme suit:

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

Vous pouvez trouver plus d'informations sur ce qui se Monitor.Enterpasse ici . Pour citer MSDN:

Permet Enterd'acquérir le moniteur sur l'objet passé en paramètre. Si un autre thread a exécuté un Enter sur l'objet mais n'a pas encore exécuté le correspondant Exit, le thread actuel se bloquera jusqu'à ce que l'autre thread libère l'objet. Il est légal que le même thread invoque Enterplus d'une fois sans le bloquer; cependant, un nombre égal d' Exitappels doit être invoqué avant que d'autres threads en attente sur l'objet se débloquent.

La Monitor.Enterméthode attendra infiniment; il n'expirera pas .

Steven
la source
15
Selon MSDN, «l’utilisation du mot clé lock (C #) ou SyncLock (Visual Basic) est généralement préférable à l’utilisation directe de la classe Monitor, à la fois parce que lock ou SyncLock est plus concis et parce que lock ou SyncLock garantit que le moniteur sous-jacent est libéré, même si le code protégé lève une exception. Ceci est accompli avec le mot-clé finally, qui exécute son bloc de code associé indépendamment du fait qu'une exception soit levée. " msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden Strydom
10
Quel est le point de la var temp = obj; ligne. comme c'est juste une référence pour commencer, à quoi sert d'en faire une autre?
priehl
11
@priehl Il permet à l'utilisateur de changer objsans que l'ensemble du système ne se bloque.
Steven
7
@Joymon finalement, chaque fonctionnalité linguistique est du sucre syntaxique. Les fonctionnalités de langage visent à rendre les développeurs plus productifs et à rendre les applications plus maintenables, tout comme la fonction de verrouillage.
Steven
2
Correct. C'est tout le but de la lockdéclaration et du moniteur: afin que vous puissiez effectuer une opération dans un thread sans avoir à vous soucier d'un autre thread qui le déblaie.
Dizzy H. Muffin
285

C'est plus simple que vous ne le pensez.

Selon Microsoft : le lockmot clé garantit qu'un thread n'entre pas dans une section critique de code tandis qu'un autre thread se trouve dans la section critique. Si un autre thread essaie d'entrer un code verrouillé, il attendra, bloquera, jusqu'à ce que l'objet soit libéré.

Le lockmot-clé appelle Enterau début du bloc et Exità la fin du bloc. lockLe mot clé gère en fait la Monitorclasse à l'arrière-plan.

Par exemple:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

Dans le code ci-dessus, le thread entre d'abord dans une section critique, puis il se verrouille obj. Lorsqu'un autre thread essaie d'entrer, il essaiera également de se verrouiller obj, qui est déjà verrouillé par le premier thread. Le deuxième thread devra attendre la libération du premier thread obj. Lorsque le premier fil sort, un autre fil se verrouille objet entre dans la section critique.

Umar Abbas
la source
9
devons-nous créer un objet factice à verrouiller ou pouvons-nous verrouiller une variable existante dans le contexte?
batmaci
9
@batmaci - Le verrouillage d'un objet factice privé séparé vous donne la garantie que personne d'autre ne verrouille cet objet. Si vous verrouillez les données et que la même donnée est visible de l'extérieur, vous perdez cette garantie.
Umar Abbas
8
Que se passe-t-il si plusieurs processus attendent la libération du verrou? Les processus en attente sont-ils mis en file d'attente afin de verrouiller la section critique dans l'ordre FIFO?
jstuardo
@jstuardo - Ils sont en file d'attente, mais la commande n'est pas garantie d'être FIFO. Consultez ce lien: albahari.com/threading/part2.aspx
Umar Abbas
Copié sans attribution de net-informations.com/faq/qk/lock.htm
Martijn Pieters
47

Non, ils ne sont pas en file d'attente, ils dorment

Une déclaration de verrouillage du formulaire

lock (x) ... 

où x est une expression d'un type de référence, équivaut précisément à

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }

Vous avez juste besoin de savoir qu'ils attendent les uns des autres, et qu'un seul thread entrera pour verrouiller le bloc, les autres attendront ...

Le moniteur est entièrement écrit en .net donc c'est assez rapide, regardez aussi la classe Moniteur avec réflecteur pour plus de détails

Arsen Mkrtchyan
la source
6
Notez que le code émis pour l' lockinstruction a légèrement changé en C # 4: blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
LukeH
@ArsenMkrt, ils ne sont pas conservés dans la file d'attente "État bloqué"? Je pense qu'il y a une différence entre le sommeil et l'état de blocage, n'est-ce pas?
Mohanavel
quelle différence voulez-vous dire @ Mohanavel?
Arsen Mkrtchyan
1
Ce n'était pas la question. La question concernait le mot clé "lock". Supposons qu'un processus entre dans une section "verrouillage". Cela signifie que le processus bloque ce morceau de code et aucun autre processus ne peut être en mesure d'entrer dans cette section jusqu'à ce que le verrou soit libéré. Eh bien ... maintenant, 2 autres processus essaient d'entrer dans le même bloc. Puisqu'il est protégé par le mot-clé "lock", ils attendront, selon ce qui a été dit dans ce forum. Lorsque le premier processus libère le verrou. Quel processus entre dans le bloc? le premier qui a essayé d'entrer ou le dernier?
jstuardo
1
Je suppose que vous voulez dire fil au lieu de processus ... si c'est le cas, alors la réponse est non, il n'y a aucune garantie que l'on entrera ... plus ici stackoverflow.com/questions/4228864/…
Arsen Mkrtchyan
29

Les verrous empêcheront les autres threads d'exécuter le code contenu dans le bloc de verrouillage. Les threads devront attendre que le thread à l'intérieur du bloc de verrouillage soit terminé et que le verrouillage soit libéré. Cela a un impact négatif sur les performances dans un environnement multithread. Si vous devez le faire, vous devez vous assurer que le code dans le bloc de verrouillage peut être traité très rapidement. Vous devriez essayer d'éviter des activités coûteuses comme l'accès à une base de données, etc.

Andrew
la source
11

L'impact sur les performances dépend de la façon dont vous verrouillez. Vous pouvez trouver une bonne liste d'optimisations ici: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

Fondamentalement, vous devriez essayer de verrouiller le moins possible, car cela met votre code d'attente en veille. Si vous avez des calculs lourds ou du code de longue durée (par exemple, le téléchargement de fichiers) dans un verrou, cela entraîne une énorme perte de performances.

Simon Woker
la source
1
Mais essayer d'écrire du code à faible verrouillage peut souvent entraîner des bogues subtils, difficiles à trouver et à corriger, même si vous êtes un expert dans le domaine. L'utilisation d'une serrure est souvent le moindre de deux maux. Vous devez verrouiller exactement autant que vous le souhaitez, ni plus, ni moins!
LukeH
1
@LukeH: Il existe certains modèles d'utilisation où le code de faible verrouillage peut être très simple et facile [ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue]. Le plus grand danger est que si les exigences évoluent au-delà de ce que ces techniques peuvent gérer, il peut être difficile d'adapter le code pour gérer cela.
supercat
Le lien est rompu.
CarenRose
8

La partie de l'instruction lock ne peut être exécutée que par un seul thread, donc tous les autres threads l'attendront indéfiniment, le thread qui détient le verrou se termine. Cela peut entraîner un soi-disant blocage.

Mr47
la source
8

L' lockinstruction est traduite en appels aux méthodes Enteret .ExitMonitor

L' lockinstruction attendra indéfiniment que l'objet de verrouillage soit libéré.

Paolo Tedesco
la source
4

le verrou est en fait la classe Monitor cachée .

Euphorique
la source