Quand est-il approprié d'utiliser la classe Monitor
ou le mot clé lock
pour la sécurité des threads en C #?
EDIT: Les réponses semblent indiquer que lock
est un raccourci pour une série d'appels au Monitor
classe. À quoi sert exactement l'appel de serrure? 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 suite à certaines informations que vous avez fournies. Puisque vous semblez bien connaître ce domaine, j'ai posté le lien suivant: Quel est le problème avec cette solution de verrouillage et de gestion des exceptions verrouillées?
Eric Lippert en parle dans son blog: Les verrous et les exceptions ne se mélangent pas
Le code équivalent diffère entre C # 4.0 et les versions 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 repose sur Monitor.Enter
le réglage atomique du drapeau lorsque le verrou est utilisé.
Et plus tôt c'était:
var temp = obj;
Monitor.Enter(temp);
try
{
body
}
finally
{
Monitor.Exit(temp);
}
Cela suppose qu'aucune exception ne soit lancée entre Monitor.Enter
et le try
. Je pense que dans le code de débogage, cette condition a été violée car le compilateur a inséré un NOP entre eux et a ainsi créé un avortement de fil entre ceux-ci.
lock
n'est qu'un raccourci pour Monitor.Enter
avec try
+ finally
et Monitor.Exit
. Utilisez une instruction de verrouillage chaque fois que cela est suffisant - si vous avez besoin de quelque chose comme TryEnter, vous devrez utiliser Monitor.
Une instruction de verrouillage est équivalente à:
Monitor.Enter(object);
try
{
// Your code here...
}
finally
{
Monitor.Exit(object);
}
Cependant, gardez à l'esprit que Monitor peut également Wait () et Pulse () , qui sont souvent utiles dans les situations de multithreading complexes.
Mettre à jour
Cependant, en C # 4, sa mise en œuvre est différente:
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 liens
Monitor
est plus flexible. Pour moi, le cas préféré d'utilisation du moniteur est lorsque vous ne voulez pas attendre votre tour, et sautez simplement:
//already executing? eff it, lets move on
if(Monitor.TryEnter(_lockObject))
{
//do stuff;
Monitor.Exit(_lockObject);
}
Comme d’autres l’ont dit, lock
est "équivalent" à
Monitor.Enter(object);
try
{
// Your code here...
}
finally
{
Monitor.Exit(object);
}
Mais juste par curiosité, lock
conservera la première référence que vous lui transmettez et ne jettera pas si vous le changez. Je sais que ce n'est pas recommandé de changer l'objet verrouillé et vous ne voulez pas le faire.
Mais encore une fois, pour la science, cela fonctionne bien:
var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
tasks.Add(Task.Run(() =>
{
Thread.Sleep(250);
lock (lockObject)
{
lockObject += "x";
}
}));
Task.WaitAll(tasks.ToArray());
... Et cela ne veut pas:
var lockObject = "";
var tasks = new List<Task>();
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é gérée dans le code utilisateur
Informations complémentaires: La méthode de synchronisation d'objet a été appelée à partir d'un bloc de code non synchronisé.
Ceci est dû au fait que Monitor.Exit(lockObject);
agira sur lockObject
, qui a changé car strings
est immuable, vous l'appelez alors à partir d'un bloc de code non synchronisé .. mais de toute façon. C'est juste un fait amusant.
Les deux sont la même chose. lock est c sharp et utilise la classe Monitor.
http://msdn.Microsoft.com/en-us/library/ms173179 (v = vs.80) .aspx
Le verrouillage et le comportement de base du moniteur (entrée + sortie) sont plus ou moins identiques, mais le moniteur dispose de 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 Pulse pour des utilisations avancées (telles que des barrières, des sémaphores, etc.).