Блокировки повторного входа в C #

119

Приведет ли следующий код к тупиковой ситуации при использовании C # в .NET?

 class MyClass
 {
    private object lockObj = new object();

    public void Foo()
    {
        lock(lockObj)
        { 
             Bar();
        }
    }

    public void Bar()
    {
        lock(lockObj)
        { 
          // Do something 
        }
    }       
 }
парень
источник
6
Не могли бы мы подумать об изменении названия этого вопроса - возможно, на что-то вроде недавно закрытого. Почему вложенные блокировки не вызывают тупик? В его нынешнем виде название кажется почти созданным для того, чтобы люди не могли его обнаружить.
Джефф Стернал,
12
На самом деле я нашел это по поисковому слову «повторно въезжающий», и он ответил на мой вопрос. Если это дублированный вопрос, это уже другая проблема ...
emfurry 05
Я согласен с комментарием @JeffSternal, этот вопрос предполагает, что человек, ищущий вопрос, уже знаком с блокировками «повторного входа». Другой вопрос о дублировании, я думаю, имел хорошее название для этого: stackoverflow.com/questions/3687505/…
Луис Перес

Ответы:

148

Нет, пока вы блокируете один и тот же объект. Рекурсивный код фактически уже имеет блокировку и поэтому может беспрепятственно продолжать работу.

lock(object) {...}- это сокращение от использования класса Monitor . Как указывает Марк , Monitorпозволяет повторный вход , поэтому повторные попытки заблокировать объект, на котором текущий поток уже имеет блокировку, будут работать нормально.

Если вы начинаете блокировать разные объекты, тогда вам нужно быть осторожным. Обратите особое внимание на:

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

Если вы нарушите любое из этих правил, в какой-то момент у вас наверняка возникнут проблемы с тупиком .

Вот одна хорошая веб-страница, описывающая синхронизацию потоков в .NET: http://dotnetdebug.net/2005/07/20/monitor-class-avoiding-deadlocks/

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

(Еще одно замечание: ваш пример технически не рекурсивен. Чтобы он был рекурсивным, Bar()он должен вызывать сам себя, как правило, как часть итерации.)

Нил Барнуэлл
источник
1
Особенно в разных последовательностях.
Марк Гравелл
6
Повторная рекурсия; на самом деле; в пользу Гая, срок повторный
Марк Грейвелл
Спасибо за пояснение терминологии - отредактировал и исправил вопрос.
Guy
Этот вопрос, кажется, привлекает довольно много внимания, поэтому я обновил свой ответ парой других замечаний, которые я придумал с тех пор, как впервые написал его.
Нил Барнуэлл,
На самом деле, я не думаю, что порядок, в котором вы снимаете блокировки, имеет значение. Порядок, в котором вы их выбираете, определенно имеет значение, но пока снятие блокировки не зависит от чего-либо (вы можете снять ее в любое время), с вами все будет в порядке, если вы освободите все блокировки, которые вы приобрели.
bobroxsox
20

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

Марк Гравелл
источник
7

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

Один и тот же поток может получать одну и ту же блокировку несколько раз, но вы должны убедиться, что вы снимаете блокировку столько же раз, сколько вы ее получаете. Конечно, если для этого используется ключевое слово «lock», это происходит автоматически.

Джеффри Л. Уитледж
источник
Обратите внимание, что это верно для мониторов, но не обязательно для других видов блокировки.
Джон Скит,
(Не желая намекать, что вы этого не знали, конечно - просто это важное различие :)
Джон Скит,
Неплохо подмечено. Я действительно собирался повсюду сменить «блокировку» на «монитор», но потом отвлекся. И ленивый. И такое поведение также верно для объектов ядра мьютексов Windows, поэтому я решил, что достаточно близко!
Джеффри Л. Уитледж,
5

Нет, в этом коде не будет мертвых замков. Если вы действительно хотите создать тупик, для простейшего потребуется минимум 2 ресурса. Рассмотрим сценарий «собака и кость». 1. Собака полностью контролирует одну кость, поэтому любой другой собаке придется ждать. 2. Две собаки с двумя костями необходимы как минимум для создания тупика, когда они, соответственно, блокируют свои кости и ищут также и другие кости.

.. и так далее, n собак и m костей и вызывают более сложные тупики.

Ришаб Джайн
источник