Повторно входящие блокировки в C # - PullRequest
111 голосов
/ 24 декабря 2008

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

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

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

    public void Bar()
    {
        lock(lockObj)
        { 
          // Do something 
        }
    }       
 }

Ответы [ 4 ]

139 голосов
/ 24 декабря 2008

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

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

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

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

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

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

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

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

19 голосов
/ 24 декабря 2008

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

6 голосов
/ 24 декабря 2008

Если поток уже удерживает блокировку, он не будет блокироваться сам. .Net Framework гарантирует это. Вам нужно только убедиться, что два потока не пытаются получить одинаковые две блокировки вне последовательности какими-либо путями кода.

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

5 голосов
/ 14 января 2012

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

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

...