Монитор против блокировки - PullRequest
80 голосов
/ 12 февраля 2011

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

РЕДАКТИРОВАТЬ: Из ответов до сих пор кажется, что 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 ]

81 голосов
/ 12 февраля 2011

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

Эквивалентный код отличается в 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 между ними и таким образом сделал прерывание потока между этими возможными.

37 голосов
/ 12 февраля 2011

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

20 голосов
/ 12 февраля 2011

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

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

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

Обновление

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

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

Спасибо CodeInChaos длякомментарии и ссылки

6 голосов
/ 19 октября 2017

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

//already executing? eff it, lets move on
if(Monitor.TryEnter(_lockObject))
{
    //do stuff;
    Monitor.Exit(_lockObject);
}
6 голосов
/ 17 августа 2016

Как уже говорили другие, 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.Threading.SynchronizationLockException' произошло в 70783sTUDIES.exe, но не было обработано в коде пользователя

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

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

4 голосов
/ 12 февраля 2011

Оба - одно и то же. Зафиксируйте ключевое слово и используйте класс Monitor.

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

3 голосов
/ 12 февраля 2011

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

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

...