Объясните код: функция блокировки c # и темы - PullRequest
5 голосов
/ 30 апреля 2010

Я использовал этот шаблон в нескольких проектах (этот фрагмент кода взят из CodeCampServer ), я понимаю, что он делает, но мне действительно интересно объяснение этого шаблона. В частности:

  1. Почему двойная проверка _dependenciesRegistered.
  2. Зачем использовать lock (Lock){}.

Спасибо.

public class DependencyRegistrarModule : IHttpModule
{
    private static bool _dependenciesRegistered;
    private static readonly object Lock = new object();

    public void Init(HttpApplication context)
    {
        context.BeginRequest += context_BeginRequest;
    }

    public void Dispose() { }

    private static void context_BeginRequest(object sender, EventArgs e)
    {
        EnsureDependenciesRegistered();
    }

    private static void EnsureDependenciesRegistered()
    {
        if (!_dependenciesRegistered)
        {
            lock (Lock)
            {
                if (!_dependenciesRegistered)
                {
                    new DependencyRegistrar().ConfigureOnStartup();
                    _dependenciesRegistered = true;
                }
            }
        }
    }
}

Ответы [ 5 ]

13 голосов
/ 30 апреля 2010

Это дважды проверенная схема блокировки .

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

Обратите внимание, что это не лучший способ сделать это .

2 голосов
/ 30 апреля 2010

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

lock(Lock) по существу является формой мьютекса; только один поток может иметь блокировку - другой должен ждать, пока блокировка не будет снята (в конце оператора lock(...) {...}).

Таким образом, в этом сценарии поток может (хотя и маловероятно) был вторым потоком в lock - поэтому каждый из них должен перепроверить, если это был секунда и работа уже выполнена.

1 голос
/ 30 апреля 2010

Блокировка запрещает запуск двух потоков ConfigureOnStartup (). Между if (! _DependenciesRegistered) и точкой, которую ConfigureOnStartup () устанавливает _dependenciesRegistered = true, другой поток может проверить, зарегистрирован ли он. Другими словами:

  1. Тема 1: _dependenciesRegistered == false
  2. Тема 2: _dependenciesRegistered == false
  3. Поток 1: ConfigureOnStartup () / _dependenciesRegistered = true;
  4. Поток 2: «не видит», что он уже зарегистрирован, поэтому снова запускает ConfigureOnStartup ().
1 голос
/ 30 апреля 2010

Схема блокировки с двойной проверкой примерно равна:

у вас есть операция, которую вы хотите условно выполнить один раз

if (needsToDoSomething) {
   DoSomething();
   needsToDoSomething = false;
}

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

lock (Lock) {
    if (needsToDoSomething) {
       DoSomething();
       needsToDoSomething = false;
    }
}

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

 if (needsToDoSomething)
    lock (Lock) {
        if (needsToDoSomething) {
           DoSomething();
           needsToDoSomething = false;
        }
    }

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

1 голос
/ 30 апреля 2010

Это вопрос производительности.

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

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