Подтвердить что ты не робот

Оператор блокировки С#, какой объект блокировать?

У меня есть 3 вопроса, с которыми мне нужна помощь.

  • Каковы правильные объекты/ссылки, которые должны быть переданы как параметр lock statement? Я видел много примеров кода, и я заметил, что переданные объекты/ссылки могут быть не связаны с текущим классом или любым другим классом в программе, если модификатор доступа static не является общедоступным? Например:.

    private Object anyObj = new Object();
    lock(anyObj){.....}
    
    private static readonly object Locker = new object();
    lock(Locker){.....}
    

    Мне это просто не имеет смысла.

  • Я нашел пример кода в MSDN о многопоточности, который также использует выражения lock. В образце есть два блока try/catch с Monitor.Wait() внутри него. Если я правильно понимаю логику, readerFlag запретит программе когда-либо вообще вводить блок try/catch.
    Код здесь пример 2:
    http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx

  • Как запустить поток, который работает в фоновом режиме, если активна форма Windows?

4b9b3361

Ответ 1

Как и что вы блокируете, зависит от того, что вы делаете.

Скажем, что вы работаете с каким-то устройством - скажем, кофеваркой. У вас может быть класс, который выглядит следующим образом:

public CoffeeMaker {
    private IntPtr _coffeeHandle;
    private Object _lock = new Object();
}

В этом случае вы защищаете доступ к _coffeeHandle - указателю/дескриптору к реальному физическому устройству, поэтому это довольно просто:

public int AvailableCups {
    get {
        lock (_lock) {
            return GetAvailableCups(_coffeeHandle); // P/Invoked
        }
    }
}

public void Dispense(int nCups)
{
    lock (_lock) {
        int nAvail = GetAvailableCups(_coffeeHandle);
        if (nAvail < nCups) throw new CoffeeException("not enough coffee.");
        Dispense(_coffeeHandle, nCups); // P/Invoked
    }
 }

Итак, если я запускаю многопоточное приложение, я (возможно) не хочу читать количество чашек, доступных во время раздачи (возможно, это аппаратная ошибка). Защищая доступ к дескриптору, я могу это обеспечить. Кроме того, меня не могут просить отказаться, пока я уже раздаю - это было бы плохо, так что тоже защищалось. Наконец, я не обойдусь, если у меня недостаточно кофе, и вы заметите, что я не использую свою публичную собственность, чтобы проверить, что - таким образом, действие обеспечения достаточного количества кофе и раздачи связано, Магическое слово атомизировано - их нельзя обрезать без возникновения проблем.

Вы используете статический объект как блокировку, если у вас есть один и только один экземпляр ресурса, который нуждается в защите. Подумайте: "У меня есть синглтон?" и это будет ориентиром для того, когда вам может понадобиться статический замок. Например, скажем, что у CoffeeMaker есть частный конструктор. Вместо этого у вас есть метод factory, который создает кофейные автоматы:

static Object _factLock = new Object();

private CoffeeMaker(IntPtr handle) { _coffeeHandle = handle; }

public static CoffeeMaker GetCoffeeMaker()
{
    lock (_factLock) {
        IntPtr _handle = GetCoffeeMakerHandle(); // P/Invoked
        if (_handle == IntPtr.Zero) return null;
        return new CoffeeMaker(_handle);
    }
 }

Теперь в этом случае, похоже, что CoffeeMaker должен реализовать IDisposable, чтобы обработать его, потому что, если вы его не отпустите то кто-то может не получить кофе.

Есть несколько проблем, хотя - возможно, если кофе недостаточно, мы должны сделать больше - и это занимает много времени. Хек - раздача кофе занимает много времени, поэтому мы стараемся защитить наши ресурсы. Теперь вы думаете, что на самом деле все эти вещи для кофеварки должны быть в своем собственном потоке и что должно произойти событие, которое увольняется, когда кофе сделан, а затем он начинает усложняться, и вы понимаете важность знания что вы запираете и когда вы не блокируете приготовление кофе, потому что вы спросили, сколько там чашек.

И если слова "тупик", "атомный", "монитор", "подождать" и "пульс" всех звуков, чуждых вам, вам следует рассмотреть возможность многопроцессорности/многопоточности в целом и посмотреть, можете ли вы решить проблема с парикмахерской или проблема философов столовой, как квинтэссенция примеры конкуренции ресурсов.

Ответ 2

1) ваш код неполный. Вы всегда блокируете определенный (общий) ресурс. anyObject должен иметь близкое соответствие 1-1 в жизни с этим общим объектом.

Например:

a) простой, но самый прямой шаблон:

List<MyClass> sharedList = ...;
...
lock (sharedList) { sharedList.Add(item); }

есть недостаток в этом шаблоне: что, если другой код также блокируется на sharedList по другим причинам? Обычно это не практическая проблема, но причина в том, что рекомендуемая модель (b):

List<MyClass> sharedList = ...;
private object listLock = new object();
...
lock (listLock) { sharedList.Add(item); }

Или, когда общий объект статичен (c):

static List<MyClass> sharedList = ...;
private static object listLock = new object();
...
lock (listLock) { sharedList.Add(item); }

2) Нитки заменяют настройку readerFlag на true или false, поэтому будут введены блоки try/catch. Синхронизация выполняется с помощью Monitor.Pulse() и .Wait(). Обратите внимание, что Wait() даст блокировку на длительность s, в которой нет взаимоблокировки.

Ответ 3

1: используемый вами объект определяет/определяется - блокировкой, которую вы пытаетесь обеспечить. Если есть "что-либо, вызывающее текущий экземпляр", тогда a private readonly object syncLock = new object() будет разумным. Если это "любой код, независимо от экземпляра" (в частности, статический,), то private readonly static object syncLock = new object(). Иногда существует очевидная "вещь", которую вы пытаетесь защитить, которая также будет служить: список, очередь и т.д. Основными неправильными решениями являются: this, typeof(...), any string, любой тип значения, который вы боксируете для каждого lock, и все, что вы просочились вне экземпляра.

2: Monitor.Wait освобождает блокировки из текущего потока, ожидая "импульса" или таймаута, после чего он просыпается и присоединяется к очереди, чтобы восстановить ее блокировки (обратите внимание, что "s" есть для повторного входа). Это означает, что два потока могут использовать Monitor для сигнализации между собой, путем пульсирования и ожидания.

3: несвязанный; но в основном "периодически проверяют флаг, а при импульсном"

Ответ 4

В соответствии с документация MSDN:

Аргумент, предоставленный ключевому слову блокировка... используется для определения области блокировки.... Строго говоря, предоставленный объект используется исключительно для уникальной идентификации ресурса, являющегося разделяемый между несколькими потоками, поэтому он может быть произвольным классом пример. На практике, однако, этот объект обычно представляет собой ресурс, для которого необходима синхронизация потоков.

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