Монитор против замка


когда уместно использовать либо Monitor или lock ключевое слово для потокобезопасности в C#?

EDIT: Кажется из ответов так далеко, что lock короткая рука для серии звонков в Monitor класса. Для чего именно нужен короткий вызов замка? Или более явно,

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 ?
    }
}

обновление

спасибо всем за помощь : я отправил еще один вопрос в продолжение некоторых информацию вы все предоставили. Поскольку вы, кажется, хорошо разбираетесь в этой области, я разместил ссылку:что не так с этим решением для блокировки и управления заблокированными исключениями?

7 72

7 ответов:

Эрик Липперт говорит об этом в своем блоге: блокировки и исключения не смешиваются

эквивалентный код отличается между C# 4.0 и более ранних версий.


в C# 4.0 это:

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

он полагается на Monitor.Enter атомарная установка флага при снятии блокировки.


а раньше было:

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

Это зависит от того, что между Monitor.Enter и try. Я думаю, что в отладке код это условие было нарушено, потому что компилятор вставил NOP между ними и, таким образом, сделал прерывание потока между ними возможным.

lock - Это просто ярлык для Monitor.Enter С try + finally и Monitor.Exit. Используйте оператор блокировки всякий раз, когда этого достаточно - если вам нужно что-то вроде TryEnter, вам придется использовать монитор.

оператор блокировки эквивалентен:

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

однако имейте в виду, что монитор также может ждать() и Pulse (), которые часто полезны в сложных многопоточных ситуациях.

обновление

однако в C# 4 его реализовали по-другому:

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

Thanx в CodeInChaos для комментариев и ссылки

как говорили другие,lock "эквивалентно"

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

но просто из любопытства, lock сохранит первую ссылку, которую вы передаете ему, и не будет бросать, если вы ее измените. Я знаю, что не рекомендуется менять заблокированный объект и вы не хотите этого делать.

но, опять же, для науки, это прекрасно работает:

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());

...И это не значит:

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());

ошибка:

исключение типа 'System.Нарезка резьбы.SynchronizationLockException' произошло в 70783студии.exe, но не был обработан в пользовательском коде

дополнительная информация: метод синхронизации объектов был вызван из несинхронизированный блок кода.

это так Monitor.Exit(lockObject); будет действовать на lockObject, который был изменен, потому что strings являются неизменяемыми, то вы вызываете его из несинхронизированного блока кода.. но все равно. Это просто забавный факт.

Это одно и то же. блокировка-это ключевое слово C sharp и класс монитора использования.

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

блокировка и основное поведение монитора (ввод + выход) более или менее одинаковы, но монитор имеет больше опций, что позволяет вам больше возможностей синхронизации.

блокировка-это ярлык, и это вариант для основного использования.

Если вам нужно больше контроля, монитор является лучшим вариантом. Вы можете использовать Wait, TryEnter и Pulse для расширенных применений (таких как барьеры, семафоры и т. д.).

Monitor более гибкая. Для меня любимым случаем использования монитора является когда вы не хотите ждать для вашей очереди, и просто пропустить:

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