Замечание Microsoft в ReaderWriterLockSlim.IsReadLockHeld/IsWriteLockHeld и его последствия

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

public class SomeClass
{
    public readonly ReaderWriterLockSlim SyncObj = new ReaderWriterLockSlim();  
    public string AProperty
    {
        get
        {
            if (SyncObj.IsReadLockHeld)
                return ComplexGetterMethod();
            SyncObj.EnterReadLock();
            try
            {
                return ComplexGetterMethod();
            }
            finally
            {
                SyncObj.ExitReadLock();
            }
        }
        set
        {
            if (SyncObj.IsWriteLockHeld)
                ComplexSetterMethod(value);
            else
            {
                SyncObj.EnterWriteLock();
                ComplexSetterMethod(value);
                SyncObj.ExitWriteLock();
            }
        }
    }

    // more properties here ...

    private string ComplexGetterMethod()
    {
        // This method is not thread-safe and reads
        // multiple values, calculates stuff, ect. 
    }

    private void ComplexSetterMethod(string newValue)    
    {
        // This method is not thread-safe and reads
        // and writes multiple values.
    }
}

// =====================================

public static SomeClass AClass = new SomeClass();
public void SomeMultiThreadFunction()
{
    ...
    // access with locking from within the setter
    AClass.AProperty = "new value";
    ...
    // locking from outside of the class to increase performance
    AClass.SyncObj.EnterWriteLock();
    AClass.AProperty = "new value 2";
    AClass.AnotherProperty = "...";
    ...
    AClass.SyncObj.ExitWriteLock();
    ...
}

Чтобы избежать ненужных блокировок всякий раз, когда я получаю или устанавливаю несколько свойств, я однажды опубликовал объект ReaderWriterLockSlim -Object и заблокировал его вне класса каждый раз, когда я собираюсь получить или установить набор свойств. Для этого мои методы getter и setter проверяют, была ли блокировка получена с использованием свойства IsReadLockHeld и свойства IsWriteLockHeld ReaderWriterLockSlim. Это отлично работает и увеличило производительность моего кода.

До сих пор так хорошо, но когда я перечитывал документацию о IsReadLockHeld и IsWriteLockHeld, я заметил форму замечаний Microsoft:

Это свойство предназначено для использования в утверждениях или для другой отладки цели. Не используйте его для управления потоком выполнения программы.

Мой вопрос: Есть ли причина, по которой я не должен использовать IsReadLockHeld/IsWriteLockHeld для этой цели? Что-то не так с моим кодом? Все работает так, как ожидалось, и намного быстрее, чем использование рекурсивных замков (LockRecursionPolicy.SupportsRecursion).

Чтобы прояснить это: это минимальный пример. Я не хочу знать, нужна ли сама блокировка или ее можно удалить или добиться другим способом. Я просто хочу знать, почему я не должен использовать IsReadLockHeld/IsWriteLockHeld для управления потоком программы, как указано в документации.

Ответ 1

После некоторых дальнейших исследований я опубликовал тот же вопрос на Немецком форуме поддержки Сети разработчиков Microsoft и обсудил с очень полезным модератором Марселем Ромой. Он смог связаться с программистом ReaderWriterLockSlim Джо Даффи, который написал этот ответ:

Я боюсь, что мой ответ может оставить желать лучшего.

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

Как правило, хорошая идея структурировать ваш код, чтобы вы либо используйте рекурсивные приобретения, или вы этого не сделаете (и, конечно, последнее всегда легче рассуждать); используя такие свойства, как IsReadLockHeld земли где-то посередине.

Я был одним из основных разработчиков RWLS, и я должен признать, что он слишком много колоколов и свистов. Я не обязательно сожалею о добавлении IsReadLockHeld - поскольку это может пригодиться для отладки и утверждений - однако, как только мы добавили его, ящик Pandora был открыт, и мы RWLS были мгновенно открыты для такого использования.

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

Подводя итог: Вы можете использовать свойство IsReadLockHeld и IsWriteLockHeld для условного блокирования, и все будет работать нормально, но это плохой стиль программирования, и его следует избегать, Лучше придерживаться рекурсивных или нерекурсивных блокировок. Для поддержания хорошего стиля кодирования IsReadLockHeld и IsWriteLockHeld следует использовать только для целей отладки.

Я хочу еще раз поблагодарить Марселя Рому и Джо Даффи за их драгоценную помощь.

Ответ 2

Документация подсказывает вам правильную вещь.

Содержит следующее выполнение с чередованием.

Thread1.AcqrireReadLock();
Thread1.ComplexGetterMethod();
Thread2.ReadIsReaderLockHeldProperty();
Thread1.ReleaseReadLock();
Thread2.ComplexGetterMethod(); // performing read without lock.

Другая неправильная вещь с вашим кодом, который я вижу, -

SyncObj.EnterReadLock();
try
{
    return ComplexGetterMethod();
}
finally
{
    SyncObj.ExitReadLock();
}

- это неправильный способ сделать что-то. Это одно право:

try
{
    SyncObj.EnterReadLock();

    return ComplexGetterMethod();
}
finally
{
    if (SyncObj.IsReadLockHeld)
        SyncObj.ExitReadLock();
}

И это будет точное определение вашего метода getter.