Замечание 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.