C # Lock заявления - PullRequest
       52

C # Lock заявления

9 голосов
/ 14 августа 2011

Когда поток пытается войти в критическую секцию и получить блокировку, что он на самом деле делает?

Я спрашиваю об этом, потому что я обычно создаю объект (типа объект), который будет служить для блокировкитолько для целей.Рассмотрим следующее: я хочу написать метод, который принимает коллекцию, и объект, который будет служить блокирующим объектом, поэтому вся манипуляция коллекцией внутри этого метода будет объявлена ​​внутри критической секции, которая будет заблокирована данным объектом.

Должен ли я передать этот блокирующий объект с помощью «ref» или достаточно передать контрольную копию этого объекта?Другими словами - поскольку оператор блокировки используется только со ссылочными типами, проверяет ли механизм значение ссылочного объекта или проверяет указатель ?потому что, очевидно, при передаче объекта без «ref», я фактически получаю копию ссылки, а не саму ссылку.

Ответы [ 4 ]

6 голосов
/ 14 августа 2011

Вот типичный шаблон, который вы можете использовать для блокировки. По сути, вы можете создать блокирующий объект, который используется для блокировки доступа к вашему критическому разделу (который, как сказал @Hans, не защищает объект, над которым вы работаете - он просто обрабатывает блокировку).

class ThreadSafe
{
  static readonly object _locker = new object();
  static int _val1, _val2;

  static void Go()
  {
    lock (_locker)
    {
      if (_val2 != 0) Console.WriteLine (_val1 / _val2);
      _val2 = 0;
    }
  }
}

Этот пример взят из онлайновой книги Джозефа Албахари о потоках. Он предоставляет превосходный обзор того, что происходит, когда вы создаете оператор lock, и некоторые советы / рекомендации о том, как лучше всего его оптимизировать. Определенно настоятельно рекомендуется к прочтению.

За Албахари, опять же, оператор lock переводит в .NET 4 как:

bool lockTaken = false;
try
{
  Monitor.Enter (_locker, ref lockTaken);
  // Do your stuff...
}
finally { if (lockTaken) Monitor.Exit (_locker); }

На самом деле это безопаснее, чем прямой Monitor.Enter, а затем вызывать Monitor.Exit в вашем finally, поэтому он был добавлен в .NET 4.

3 голосов
/ 14 августа 2011

Достаточно заблокировать object без прохождения ref. Что на самом деле делает * 1003, так это звоните Monitor.Enter в начале блока и Monitor.Exit при выходе.

Надеюсь, это поможет.

1 голос
/ 14 августа 2011

Должен ли я передать этот блокирующий объект с помощью «ref» или достаточно передать контрольную копию этого объекта?

Вероятно, ни того, ни другого.Если у вас есть какой-то ресурс, который не является потокобезопасным, обычно лучше всего получить доступ к этому ресурсу напрямую только из одного класса, который имеет в качестве поля объект блокировки (или вы можете заблокировать его непосредственно на ресурсе).Если вы передаете объект блокировки другим, трудно убедиться, что код по-прежнему будет работать должным образом, например, блокировка выполняется тогда, когда это необходимо, и нет тупиков.

Но если вы действительно хотите передать блокировкуобъект, вам не нужно использовать ref, как указали другие.Блокировка выполняется для экземпляра объекта, а не для переменной, содержащей ссылку на него.

1 голос
/ 14 августа 2011

MSDN сообщает здесь о блокировке

Используйте Enter, чтобы получить монитор для объекта, переданного в качестве параметра.Если другой поток выполнил Enter на объекте, но еще не выполнил соответствующий Exit, текущий поток заблокируется, пока другой поток не освободит объект.Для одного потока допустимо несколько раз вызывать Enter без его блокировки;однако перед тем, как другие потоки, ожидающие объекта, будут разблокированы, должно быть вызвано равное количество вызовов Exit.

, что означает, что речь идет не о ссылке или указателе, а о фактическом объекте, на который указывает объект.ссылка, так что вам не нужно будет передавать, поскольку ref простой переход по ссылке будет работать

Относительно того, что на самом деле происходит внутри замка, см. ответ на этот вопрос, который говорит

"Оператор блокировки переводится C # на следующее:"

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}
...