Я написал довольно банальную обертку вокруг ReaderWriterLockSlim
:
class SimpleReaderWriterLock
{
private class Guard : IDisposable
{
public Guard(Action action)
{
_Action = action;
}
public void Dispose()
{
_Action?.Invoke();
_Action = null;
}
private Action _Action;
}
private readonly ReaderWriterLockSlim _Lock
= new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
public IDisposable ReadLocked()
{
_Lock.EnterReadLock();
return new Guard(_Lock.ExitReadLock);
}
public IDisposable WriteLocked()
{
_Lock.EnterWriteLock();
return new Guard(_Lock.ExitWriteLock);
}
public IDisposable UpgradableReadLocked()
{
_Lock.EnterUpgradeableReadLock();
return new Guard(_Lock.ExitUpgradeableReadLock);
}
}
(Вероятно, это не самая эффективная вещь в мире, поэтому меня также интересуют предлагаемые улучшения для этого класса.)
Используется так:
using (_Lock.ReadLocked())
{
// protected code
}
(Значительное число операций чтения происходит очень часто, и почти никогда не происходит никаких записей.)
Кажется, что это всегда работает должным образом в режиме выпуска и в производстве. Однако в режиме отладки и в отладчике очень редко процесс блокируется в особом состоянии - он вызвал EnterReadLock
, сама блокировка не удерживается ничем (владельцем является 0, свойства, которые сообщают, есть ли у него читатели) / писатели / официанты не говорят и т. д.) , но блокировка вращения внутри заблокирована, и она там бесконечно вращается.
Я не знаю, что вызывает это, за исключением того, что, кажется, это происходит чаще, если я останавливаюсь на точках останова и пошагово (в совершенно не связанном коде).
Если я вручную переключу поле спинблока _isLocked
обратно на 0, то процесс возобновится, и после этого все будет работать так, как ожидалось.
Что-то не так с кодом или с самой блокировкой? Делает ли отладчик что-то, чтобы случайно вызвать взаимоблокировку спин-блокировки? (Я использую .NET 4.6.2.)
Я прочитал статью , которая указывает, что ThreadAbortException
может быть проблемой для этих блокировок - и мой код действительно имеет вызовы Abort()
в некоторых местах - но я не думаю те, которые включают в себя код, который вызывает этот заблокированный код (хотя я могу ошибаться), и если проблема была в том, что блокировка была получена и никогда не снималась, то это должно выглядеть не так, как я вижу. (Несмотря на это, в документации по фреймворку специально запрещается получение блокировки в ограниченном регионе, как рекомендуется в этой статье.)
Я могу изменить код, чтобы избежать перенаправления блокировки, но разве using
не защищает рекомендуемую практику в целом?