Рекурсивная/вложенная блокировка в С# с оператором блокировки

Возможный дубликат:
Заблокированные блокировки на С#

Я посмотрел здесь на StackOverflow и на MSDN, и не могу поверить, что я не мог найти этот вопрос затяжным там, в интернетах.

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

public class MyClass
{
    private Object SomeSharedData = new Object();

    public void MethodA()
    {
        lock( SomeSharedData) {
            // do something
            MethodB();
        }
    }

    public void MethodB()
    {
        lock( SomeSharedData) {
            // do something
        }
    }
}

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

Гарантировано ли это безопасно? Другими словами,.NET обрабатывает это по ссылке, подсчитывая блокировку, так что, когда я выхожу из этих методов, блокировка уменьшается? Или .NET выполняет некоторую магию за кулисами, посредством чего она просто игнорирует все последующие блокировки объекта, происходящего из того же потока?

Ответ 1

Да, блокировки на основе Monitor в .NET являются рекурсивными и считаются.

Из документов для Monitor.Enter:

Для одного и того же потока invoke Введите несколько раз без него блокирование; однако, равное количество Вызывные вызовы должны быть вызваны до другие потоки, ожидающие объекта разблокируется.

Будет ли это хорошо или нет, для обсуждения...

Ответ 2

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

Например, ReaderWriterLockSlim по умолчанию не поддерживает рекурсию, и этот фрагмент кода генерирует исключение:

public class MyClass
{
    ReaderWriterLockSlim rw = new ReaderWriterLockSlim();
    //You should explicitly stated that you want to use recursion
    ReaderWriterLockSlim rwWithRecursion = new ReaderWriterLockSlim (LockRecursionPolicy.SupportsRecursion);

    public void MethodA()
    {
        try {
           rw.EnterReadLock();
           // do something
           MethodB();
        }
        finally {
          rw.ExitReadLock();
        }
    }

    public void MethodB()
    {
        try {
           rw.EnterReadLock(); //throws LockRecursionException
        }
        finally {
          rw.ExitReadLock();
        }
    }
}