我发现,有时候它是非常有用的—主要是为了避免过度缩进,但偶尔也是为了其他目的—能够使用Monitor.Enter
和Monitor.Exit
而不是嵌入在IDisposable
实现中的lock
关键字.
然而,这会引发一个简单的bug,据我所知,C#在没有帮助的情况下无法在编译时捕捉到——也就是说,如果你在一个可能在不同线程上运行不同代码块的Systemc方法中不适当地使用了这种技术,就不能保证Monitor.Exit
会在同一线程上被调用.
由于这是我自己的代码,到目前为止,我已经采取了一种由来已久的技术,即"你能做的愚蠢的事情?"不要这样做",但我想要一些更好的导轨,如果他们可用.我想到的是一个类似于属性的东西,我可以把它放在一个方法上,它基本上说:"当在一个javoc代码块中时,不允许调用这个方法".
这样的东西存在吗?
请注意,我并不希望被指向信号量—问题不是解决方案的 Select ,而是我是否可以添加指南,以使程序无法编译,或者LockHelper构造函数或方法在这种情况下可能由于疏忽而出现时会失败.
问题的例子:
LockHelper类
public struct LockHelper : IDisposable
{
private readonly object _target;
private bool _locked;
private bool _disposed;
public LockHelper(object target, bool startLocked = true)
{
_target = target;
_locked = false;
_disposed = false;
if (startLocked)
Lock();
}
private void Lock()
{
AssertNotDisposed();
if (_locked)
return;
_locked = true;
Monitor.Enter(_target);
}
private void AssertNotDisposed()
{
if (!_disposed)
return;
throw new ObjectDisposedException(nameof(LockHelper));
}
private void Unlock()
{
AssertNotDisposed();
if (!_locked)
return;
_locked = false;
Monitor.Exit(_target);
}
public void Dispose()
{
if (_disposed)
return;
Unlock();
_disposed = true;
}
}
不正确的用法
readonly object _sync = new object();
readonly IAsyncService _service;
async Task DoSomething()
{
// Start on threat #123
using var lh = new LockHelper(_sync);
await _service.SomeAsyncCall();
// Resume on some other threadpool thread, call it #456
// That means the _sync is still locked by #123, but we have NO WAY to unlock it
// Therefore disposal of #123 will happen on that other thread, myriad other issues
}