我写了一个相当简单的包装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
守卫推荐的做法吗?