Разница между блокировкой (locker) и блокировкой (variable_which_I_am_using) - PullRequest
30 голосов
/ 23 октября 2008

Я использую C # & .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.

Ответы [ 8 ]

28 голосов
/ 23 октября 2008

Опция 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
}

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

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

lock (m_LockerA)
{
    // It's 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's possible this block is active in one thread
    // while the block above is active in another
    // Do something with the dictionary
}

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

11 голосов
/ 23 октября 2008

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

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

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

8 голосов
/ 19 января 2012

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

3 голосов
/ 23 октября 2008

Важно не то, что вы «блокируете», а код, который находится между блокировкой {...}, и то, что вы препятствуете его выполнению.

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

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

2 голосов
/ 23 октября 2008

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

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

1 голос
/ 23 октября 2008

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

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

0 голосов
/ 21 октября 2015

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

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

0 голосов
/ 23 октября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...