Объясните код: функция блокировки c# и потоки

Я использовал этот шаблон в нескольких проектах, (это отрезало кода, от 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
задан Fitzchak Yitzchaki 29 April 2010 в 21:10
поделиться

5 ответов

Это шаблон блокировки с двойной проверкой.

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

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

13
ответ дан 18 December 2019 в 09:05
поделиться

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

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

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

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

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

2
ответ дан 18 December 2019 в 09:05
поделиться

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

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

1
ответ дан 18 December 2019 в 09:05
поделиться

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

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

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

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

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

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

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

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

1
ответ дан 18 December 2019 в 09:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: