.net Dispose不必是线程安全的

.net Dispose不必是线程安全的,.net,multithreading,garbage-collection,dispose,.net,Multithreading,Garbage Collection,Dispose,在通过C讨论Jeffrey Richter的CLR中的Dispose方法时,作者指出: 但是,设计指南规定,处置不必 线程安全。原因是,只有在代码知道的情况下,代码才应该调用Dispose 因为没有其他线程在使用该对象 这听起来与我的直觉背道而驰。当然,如果一个类包装了一个本机或托管资源,那么一旦调用了Dispose,它应该确保在试图释放资源的同时,没有其他调用方正在使用该资源。在我看来,包装器知道有多少呼叫者在使用资源,与所有可能不知道彼此的不同呼叫者相比,它在同步Dispose方面的工作要容

在通过C讨论Jeffrey Richter的CLR中的Dispose方法时,作者指出:

但是,设计指南规定,处置不必 线程安全。原因是,只有在代码知道的情况下,代码才应该调用Dispose 因为没有其他线程在使用该对象

这听起来与我的直觉背道而驰。当然,如果一个类包装了一个本机或托管资源,那么一旦调用了Dispose,它应该确保在试图释放资源的同时,没有其他调用方正在使用该资源。在我看来,包装器知道有多少呼叫者在使用资源,与所有可能不知道彼此的不同呼叫者相比,它在同步Dispose方面的工作要容易得多。也有类似的问题,但他们还没有一个明确的答案


这个设计指南背后的理由是什么?

Dispose经常忽略的一个关键点是,它的目的不是对被处置的对象做任何事情,而是允许被处置的对象通知外部实体不再需要它们的服务。在许多情况下,不同线程使用的许多对象可能共享外部实体。因此,在许多情况下,外部实体能够同时从不同线程发出多个通知或请求是很重要的。如果外部实体使用池来管理资源,它可能不需要担心对特定池项同时执行的操作,但它必须做好准备,以防可能同时收到多个请求,从同一池分配或释放项

第二点是,某些类型的通信库没有任何非阻塞方法,只支持一种多线程场景:阻塞的操作可能会被任何线程异步中止。以这种方式中止操作将使通道处于未定义状态;在关闭并重新打开通道之前,通道将无法再次使用。由于这样的中止将使通道处于无用状态,因此在发出中止后,没有理由继续保留与其关联的任何资源。因此,一种常见且有用的模式是使用Dispose本身执行中止。这样的中止必须从与对象主线程分离的线程运行[如果对象的主线程被阻止,它不能运行任何用户代码来触发中止!]它必须是线程安全的。请注意,实际的清理行为可能不一定在调用Dispose的线程上运行。如果在对象在其主线程上运行代码时调用Dispose,或者该代码在对象的某个方法中被阻止,则可以设置一个标志以强制其主线程执行清理;如果在对象自己的线程未执行其代码时调用它,则对象可能会让调用Dispose的线程成为其主线程,然后该线程可以清理它;如果它以前的主线程试图在清理过程中执行某些操作,那么这样的操作将立即失败


尽管在大多数情况下,当对象仍在使用时,不应对其调用Dispose,但在某些情况下,如上面所述,这将代表一种必要的使用场景。将资源从消费者下面抽出来可能是迫使消费者关闭的一种相当粗糙的方式,但在许多情况下,这可能比任何可用的替代方案都更安全。此外,即使在不应处置对象但无论如何都不应处置的情况下,处置方法也应努力限制此类处置可能造成的伤害。如果在对象仍在使用时对其调用Dispose会导致对象上的操作以不可预知的方式失败,则可以这样做。如果对仍在使用的一个对象调用Dispose会导致为创建的下一个对象提供第一个对象仍在使用的资源,这就不太好了。无论Dispose是否以真正线程安全的方式运行,它都应确保由不当使用引起的伤害仅限于被处置的对象。

您如何看待这一点?一个线程在释放对象时使用该对象?还是两个物体处理同一个物体?对我来说这两个都没有意义。你认为包装器可以很容易地知道哪些代码包含对它的引用,这是完全错误的。这种知识需要客户代码的帮助,引用计数是标准方法。但这只是用另一个合同代替了一个合同,如果客户机代码不符合要求,合同也很容易失败。泄漏是一个标准的引用计数错误,很难修复。@PatrickHofman如果一个线程使用一个对象,而另一个线程正在尝试处理它,dispose方法可能会返回一个异常,即该资源当前正在使用。或者,它可以在资源释放时提供回调来调用。ot
她的两个对象同时处理的场景更容易处理,因为dispose方法应该允许多个调用而没有副作用。@HansPassant如果是这样,那么支持SafeWaitHandles的理由是什么?这些包装是为了确保安全处理把手。它们进行引用计数并提供特殊的终结功能。为什么Dispose方法不能遵循此模式?如果在另一个线程使用对象时处理该对象,则会出现错误。我明白了,没有必要让界面设计复杂化,从而使有缺陷的程序稍微降低崩溃的可能性。谢谢你的回答。您对该语句的具体看法是:只有当代码知道没有其他线程正在使用该对象时,代码才应该调用Dispose。@FarhadAlizadehNoori:我将其重新表述为:只有当代码知道可能正在使用该对象的任何线程应该立即停止操作时,代码才应该调用Dispose,而且,可能正在使用该对象的任何线程的契约都要求它们为该对象突然变得不可用做好准备[如果没有其他线程,这两个条件将自动得到满足]。将资源从线程下拉出是不礼貌的,但是如果线程在I/O上被阻塞,那么只有thread.Interrupt和thread.Abort更糟糕。