Surveiller vs verrou

Quand est-il approprié d’utiliser la classe Monitor ou le mot clé lock pour la sécurité des threads en C #?

EDIT: D’après les réponses, le lock est un raccourci pour une série d’appels à la classe Monitor . À quoi sert exactement la serrure à court terme? Ou plus explicitement,

 class LockVsMonitor { private readonly object LockObject = new object(); public void DoThreadSafeSomethingWithLock(Action action) { lock (LockObject) { action.Invoke(); } } public void DoThreadSafeSomethingWithMonitor(Action action) { // What goes here ? } } 

Mettre à jour

Merci à tous pour votre aide: J’ai posté une autre question pour donner suite à certaines des informations que vous avez toutes fournies. Comme vous semblez bien connaître ce domaine, j’ai posté le lien: Qu’est-ce qui ne va pas avec cette solution de locking et de gestion des exceptions verrouillées?

Eric Lippert en parle dans son blog: les serrures et les exceptions ne se mélangent pas

Le code équivalent diffère entre les versions C # 4.0 et antérieures.


En C # 4.0 c’est:

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

Il s’appuie sur Monitor.Enter définissant le drapeau de Monitor.Enter atomique lorsque le verrou est pris.


Et plus tôt c’était:

 var temp = obj; Monitor.Enter(temp); try { body } finally { Monitor.Exit(temp); } 

Cela repose sur l’absence de toute exception entre Monitor.Enter et le try . Je pense que dans le code de débogage cette condition a été violée parce que le compilateur a inséré un NOP entre eux et a ainsi fait avorter les threads entre ceux qui étaient possibles.

lock est juste un raccourci pour Monitor.Enter avec try + finally et Monitor.Exit . Utilisez une instruction de locking chaque fois que cela suffit – si vous avez besoin de quelque chose comme TryEnter, vous devrez utiliser Monitor.

Une instruction de locking est équivalente à:

 Monitor.Enter(object); try { // Your code here... } finally { Monitor.Exit(object); } 

Cependant, gardez à l’esprit que Monitor peut également attendre () et Pulse () , qui sont souvent utiles dans des situations complexes de multithreading.

Mettre à jour

Cependant, dans C # 4, il est implémenté différemment:

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

Merci à CodeInChaos pour les commentaires et les liens

Comme d’autres l’ont dit, le lock est «équivalent» à

 Monitor.Enter(object); try { // Your code here... } finally { Monitor.Exit(object); } 

Mais par curiosité, lock conservera la première référence que vous lui passerez et ne le lancera pas si vous la changez. Je sais que ce n’est pas recommandé de changer l’object verrouillé et vous ne voulez pas le faire.

Mais encore une fois, pour la science, cela fonctionne bien:

 var lockObject = ""; var tasks = new List(); for (var i = 0; i < 10; i++) tasks.Add(Task.Run(() => { Thread.Sleep(250); lock (lockObject) { lockObject += "x"; } })); Task.WaitAll(tasks.ToArray()); 

… Et ça ne veut pas:

 var lockObject = ""; var tasks = new List(); for (var i = 0; i < 10; i++) tasks.Add(Task.Run(() => { Thread.Sleep(250); Monitor.Enter(lockObject); try { lockObject += "x"; } finally { Monitor.Exit(lockObject); } })); Task.WaitAll(tasks.ToArray()); 

Erreur:

Une exception de type ‘System.Threading.SynchronizationLockException’ s’est produite dans 70783sTUDIES.exe mais n’a pas été traitée dans le code utilisateur

Informations supplémentaires: La méthode de synchronisation des objects a été appelée à partir d’un bloc de code non synchronisé.

Cela est dû au fait que Monitor.Exit(lockObject); agira sur lockObject qui a changé car les ssortingngs sont immuables, alors vous l’appelez à partir d’un bloc de code non synchronisé .. mais de toute façon. Ceci est juste un fait amusant.

Les deux sont la même chose. lock est le mot clé c sharp et utilise la classe Monitor.

http://msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx

Le locking et le comportement de base du moniteur (enter + exit) sont plus ou moins les mêmes, mais le moniteur a plus d’options qui vous offrent davantage de possibilités de synchronisation.

Le verrou est un raccourci, et c’est l’option pour l’utilisation de base.

Si vous avez besoin de plus de contrôle, le moniteur est la meilleure option. Vous pouvez utiliser Wait, TryEnter et the Pulse pour les utilisations avancées (comme les barrières, les sémaphores, etc.).

Monitor est plus flexible. Pour moi, le cas préféré de l’utilisation du moniteur, c’est lorsque vous ne voulez pas attendre votre tour et que vous sautez simplement :

 //already executing? eff it, lets move on if(Monitor.TryEnter(_lockObject)) { //do stuff; Monitor.Exit(_lockObject); }