web-dev-qa-db-fra.com

Moniteur vs verrouiller

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?

80
smartcaveman

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.

82
CodesInChaos

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.

38
Lukáš Novotný

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

20
Shekhar_Pro

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);
}
8
Alex

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.

6
André Pena

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

4
RobertoBr

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.).

3
Borja