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

Разница между блокировкой (locker) и блокировкой (variable_which_I_am_using)

Я использую С# и .NEt 3.5. В чем разница между OptionA и OptionB?

class MyClass
{
    private object m_Locker = new object();
    private Dicionary<string, object> m_Hash = new Dictionary<string, object>();

    public void OptionA()
    {
        lock(m_Locker){ 
          // Do something with the dictionary
        }
    }

    public void OptionB()
    {
        lock(m_Hash){ 
          // Do something with the dictionary
        }
    }       
}

Я начинаю разбираться в потоковой обработке (прежде всего для создания кеша для многопоточного приложения, НЕ использующего класс HttpCache, поскольку он не привязан к веб-сайту), и я вижу синтаксис OptionA во многих примеры, которые я вижу в Интернете, но я не понимаю, что, если есть, причина, которая выполняется над OptionB.

4b9b3361

Ответ 1

Вариант B использует защищаемый объект для создания критического раздела. В некоторых случаях это более четко передает намерение. Если использовать последовательно, он гарантирует, что только один критический раздел для защищаемого объекта будет активным одновременно:

lock (m_Hash)
{
    // Across all threads, I can be in one and only one of these two blocks
    // Do something with the dictionary
}
lock (m_Hash)
{
    // Across all threads, I can be in one and only one of these two blocks
    // Do something with the dictionary
}

Вариант A менее ограничительный. Он использует вторичный объект для создания критического раздела для объекта, который должен быть защищен. Если используется несколько вторичных объектов, возможно иметь более одного критического раздела для защищенного объекта, активного за раз.

private object m_LockerA = new object();
private object m_LockerB = new object();

lock (m_LockerA)
{
    // It possible this block is active in one thread
    // while the block below is active in another
    // Do something with the dictionary
}
lock (m_LockerB)
{
    // It possible this block is active in one thread
    // while the block above is active in another
    // Do something with the dictionary
}

Вариант A эквивалентен варианту B, если вы используете только один вторичный объект. Что касается кода чтения, то намерение Option B более четкое. Если вы защищаете несколько объектов, вариант B на самом деле не является вариантом.

Ответ 2

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

Одной из причин использования опции A является то, что классы, вероятно, будут иметь частные переменные, которые будут использоваться внутри оператора блокировки. Гораздо проще просто использовать один объект, который вы используете, чтобы заблокировать доступ ко всем из них, вместо того, чтобы пытаться использовать более мелкие блокировки зерна для блокировки доступа только к тем членам, которые вам понадобятся. Если вы попытаетесь пойти с более тонким методом, вам, вероятно, придется столкнуться с несколькими замками в некоторых ситуациях, а затем вам нужно убедиться, что вы всегда принимаете их в том же порядке, чтобы избежать взаимоблокировок.

Другая причина использования опции A заключается в том, что возможно, что ссылка на m_Hash будет доступна вне вашего класса. Возможно, у вас есть общедоступное свойство, которое обеспечивает доступ к нему, или, может быть, вы объявляете его защищенным, а производные классы могут его использовать. В любом случае, когда внешний код имеет ссылку на него, возможно, что внешний код будет использовать его для блокировки. Это также открывает возможность взаимоблокировок, поскольку у вас нет возможности контролировать или знать, в каком порядке будет выполняться блокировка.

Ответ 3

На самом деле, не рекомендуется блокировать объект, если вы используете его члены. Джеффри Рихтер написал в своей книге "CLR via С#", что нет гарантии, что класс объекта, который вы используете для синхронизации, не будет использовать lock(this) в своей реализации (это интересно, но это был рекомендуемый способ синхронизации с Microsoft в течение некоторого времени... Затем они обнаружили, что это была ошибка), поэтому всегда полезно использовать специальный отдельный объект для синхронизации. Таким образом, как вы видите, OptionB не даст вам гарантии тупиковой ситуации - безопасности. Таким образом, OptionA намного безопаснее, чем OptionB.

Ответ 4

Это не то, что вы "Блокировка", его код, содержащийся между блокировкой {...}, является важным и что вы предотвращаете выполнение.

Если один поток выдает блокировку() для любого объекта, он предотвращает получение другими потоками блокировки на одном и том же объекте и, следовательно, предотвращает выполнение второго потока кода между фигурными скобками.

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

Ответ 5

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

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

Ответ 6

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

Обычно я бы выбрал OptionB для обеспечения поточного доступа к m_Hash ТОЛЬКО. Если в качестве OptionA я использовал бы для типа значения блокировки, который нельзя использовать с блокировкой, или у меня была группа объектов, которые должны блокировать одновременно, но мне не нужно блокировать весь экземпляр с помощью lock(this)

Ответ 7

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

Ответ 8

OptionA - это путь, который можно использовать здесь, если во всем вашем коде при доступе к m_hash вы используете m_Locker для его блокировки.

Теперь представьте этот случай. Вы блокируете объект. И этот объект в одной из функций, которые вы вызываете, имеет сегмент кода lock(this). В этом случае это непревзойденный неисправимый тупик.