为什么CLR需要专用线程来调用finalize方法?

时间:2014-07-03 06:45:48

标签: c# garbage-collection

这是msdn文章的摘录。文章可以找到here

  

有一个专门用于调用Finalize方法的特殊运行时线程。当可释放队列为空(通常是这种情况)时,该线程会休眠。但是当条目出现时,此线程会唤醒,从队列中删除每个条目,并调用每个对象的Finalize方法。因此,您不应该在Finalize方法中执行任何代码,该方法对正在执行代码的线程做出任何假设。例如,避免在Finalize方法中访问线程本地存储。

这是我之前提出过的一个问题,我因为同样的说法没有专门的线程来处理Finalizable对象,而且我的信息源是错误的。如果本文解释的是真的,我想知道为什么CLR需要一个特殊的线程来调用Finalize方法?

3 个答案:

答案 0 :(得分:3)

没有关于什么终结器需要单独的线程,但请考虑这一点:

  • 当GC在第0代和第1代上运行时,“短暂”代,托管线程被暂停
  • 在托管线程恢复后,并发或后台集合可以在第2代上运行(在MSDN中有很多的相关信息:Fundamentals of Garbage Collection

然而,一个看似合理的天真解决方案是做以下事情:

  • 暂停所有线程(或者可能离开触发GC运行的线程,只需让它做GC)
  • 进行垃圾收集,包含所有内容
  • 恢复所有主题

这可能很容易实现。 GC操作可以自由访问与堆相关的所有数据结构,并且可以随意执行任何操作,因为没有其他访问这些内容的线程正在运行。

但是,这会使程序暂时“长时间”暂停,并且在大多数应用程序中都是非常不可接受的。

因此,在GC上工作的团队选择了暂停最小可能时间的解决方案,仅在需要时暂停(第0代和第1代集合),同时在收集第2代时让应用程序继续运行。这更难。 注意,这是我的假设,我无法访问实际的设计文档或任何有关此信息的可靠信息。这些假设基于官方文档。

回到终结者线程。这些对象已经“死”并且已经计划好收集,所以代替上面的设计决定,为什么要打扰其中一个主线程做这个应用程序?由于GC已经在后台执行操作,因此以相同的方式处理死对象的最终确定似乎是一个很好的决定。

答案 1 :(得分:0)

  1. 终结器是你的最后一道防线 - 如果你的资源没有妥善处理,那么CLR本身将会在某个地方尝试完成清理工作。这个过程完全不确定 - 它不是RAII。它不是设计用于处理某些事件处理程序取消或类似的东西 - 它应该clean the unmanaged resources您的实例使用并且无法正确处理(托管资源will be disposed,即使没有直接终结器,至少如果有不是任何循环依赖)。因此,不需要在您的工作线程上执行它。
  2. 整体垃圾收集对你的程序是不确定的和不显眼的 - 它发生在CLR决定这样做但你不知道它并且不能真正影响它的时候(好吧,有 GC class,但仍然如此)。
  3. 非托管资源可能非常庞大,应尽快处理。
  4. 即使需要在同一个线程上执行终结器,那么它又如何实际实现呢?要在某个任意时刻执行线程上的代码,[thread]必须提供某种SyncronizationContext而不是everyone can provide it。因此每个线程都有had to provide个SyncronizationContext - not the best idea
  5. 考虑到所有这些观察结果,用于最终确定的专用线程看起来非常合理且有效。

答案 2 :(得分:0)

您可以通过利用并发性来提高性能,尤其是在具有多个CPU核心的现代处理器体系结构上。垃圾收集非常有用,但它可能会对性能产生一些影响,因为当垃圾收集器重新排列内存中对象的布局时,您的应用程序基本上无法执行任何操作。但是,任何最终化都可以在后台执行,这就是为什么它是在一个单独的线程上完成的,以提高性能。