IDisposable、ObjectDisposedException和线程安全类型

6
在一个旨在实现线程安全的类型中,是否有必要跟踪经典的bool disposed字段,以便在所有主要暴露的方法之前有条件地引发ObjectDisposedException?我看到一些在线文档推荐这种模式,但我不确定作者是否使用正确,所以这个问题假设他们是正确的。
在这种情况下,似乎确保disposed状态为真的唯一方法是使用同步机制,如lock()覆盖每个公开成员的整个方法体,包括Dispose(bool)方法。那么这不会让类型再次有效地变成单线程吗?
如果是这样的话,那么使用它就没有意义了,因此在某些IDisposable实现中就不能依赖ObjectDisposedException机制。那么,如果这个机制不是必要的,我们为什么要使用它呢?
总之,在线程安全的类型中,IDisposable和ObjectDisposedException可能并不适用。

参见:https://dev59.com/wHVC5IYBdhLWcg3w1E1q - Jason Kleban
4个回答

4
也许更有效的使一个线程安全的对象在方法运行时不被释放的方法是使用ReaderWriterLockSlim。所有公共方法在执行时都要获取读锁,并在完成后释放它。在Dispose中获取写锁。它将等待所有其他方法完成后再获取其写锁。然后,它在写锁内设置isDisposed,该锁由它独占。在Dispose完成后调用任何公共方法都可以看到isDisposed并抛出ObjectDisposedExceptionReaderWriterLockSlim http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx

厉害的想法。有样例吗?找不到关于ReaderWriterSlimLock的文档。这是你之前见过的东西还是你刚刚构思出来的? - Jason Kleban
只是一些头脑风暴,但我认为它应该很适合你的请求。我在原始答案中拼错了它,它是 ReaderWriterLockSlim。我添加了一个链接到 MSDN 文档。 - Samuel Neff
实际上,那几乎可以工作,除了 FXcop 正确地指出 ReaderWriterLockSlim 实现了 IDisposable 接口,并且应该在对象的其余资源一起被释放。所以一旦调用 dispose 方法,我将不能再获取读取锁以测试对象是否已经被释放或没有。 - Jason Kleban
我不认为这个问题已经解决,但这是一个很好的尝试,我也不想让这个问题一直开着。 - Jason Kleban

3
如果您的对象已被处理并且其行为将有所不同,而且很可能在处理后仍会使用该对象,则需要跟踪此情况。最好抛出 ObjectDisposedException 异常,而不是随意抛出任何异常,如果您没有先检查对象是否已被处理。请注意保留 HTML 标记。

是的,但这个问题是关于如何在多线程场景下确定地抛出ObjectDisposedException的。 - Jason Kleban

0

鉴于“Disposed”布尔值只在一个地方更新,如果调用方在调用Disposed后继续使用对象,则这是一个错误。

我认为在调用Dispose后,“大多数情况下”抛出ObjectDisposedException已经足够了。我认为ObjectDisposedException是一个调试助手,而不是调用方应该捕获的东西。


0
如果在对象被处理时可能会调用某个方法,则应该以这样的方式定义该方法的语义,即在已处理的对象上调用它不会造成问题。在可能存在问题或不存在问题的情况下,应使用“尝试/执行”模式。如果 Microsoft 在 Control.BeginInvoke 中遵循了这个原则,那么就会有一个“Control.BeginInvoke”和一个“Control.TryBeginInvoke”,后者将明确定义为在操作被排队之前如果控件已被处理,则返回 false 并且不执行任何操作(请注意,Control.BeginInvoke 返回 true 不保证控件在实际运行操作之前不会被处理)。这种模式在显示更新场景等方面非常有用:如果控件未被处理,我希望其更新例程运行;但是,如果在更新例程运行之前处理了它,则更新将变得无意义,并且其未能运行也几乎不会成为问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接