在回答有关SO的另一个问题*以及随后的评论讨论时,我碰到了一个我不清楚的观点。

在我误入歧途的任何地方纠正我...

垃圾收集器收集对象时,会在单独的线程上调用该对象的终结器(除非终结器已被抑制,例如通过Dispose()方法)。在收集时,GC会挂起所有线程,除了触发该收集的线程(不包括后台收集)。

尚不清楚:

  • 垃圾收集器在收集该对象之前是否等待该终结器执行?
  • 如果不是,终结器仍在执行时是否取消线程的暂停?
  • 如果确实等待,则终结器遇到被挂起的线程之一持有的锁时会发生什么情况?终结器线程是否死锁? (在我的回答中,我认为这是错误的设计,但我可能会看到可能发生这种情况的情况)

  • *链接到原始问题:
    .NET GC Accessing a synchronised object from a finalizer

    最佳答案



    您的问题有点模棱两可。

    当GC遇到需要终结的“死”对象时,它将放弃尝试回收死对象的存储的尝试。相反,它将对象放在“我知道需要终结的对象”队列中,并将该对象视为 Activity 对象,直到完成终结器线程为止。

    因此,是的,GC会“等待”直到执行终结器,然后再回收存储。但是它不会同步等待。听起来您在问“GC是否在此处同步调用终结器?”不,它使要排队的对象排队,以便日后最终确定并继续进行。 GC希望快速完成释放垃圾和压缩内存的任务,以便适当的程序可以尽快恢复运行。它不会停止处理一些在清理之前需要引起关注的抱怨对象。它将该对象放在队列中,并说“保持安静,终结器线程将在以后处理您”。

    稍后,GC将再次检查该对象并说“您是否还死了?终结器是否运行?”如果答案为"is",则回收该对象。 (请记住,终结器可能会将死掉的对象变成活的对象;尝试永远不要这样做。结果,没有令人愉快的事情发生。)



    我相信GC解冻了冻结的线程,并向终结器线程发出信号“嘿,您有工作要做”。因此,当终结器线程开始运行时,被GC冻结的线程将再次启动。

    可能必须有未冻结的线程,因为终结器可能需要将调用编码到用户线程才能释放线程关联的资源。当然,其中一些用户线程可能会被阻塞或冻结。线程总是可以被某些东西阻塞。



    完全正确。终结器线程没有任何魔术可以防止死锁。如果用户线程正在等待终结器线程获取的锁,而终结器线程正在等待用户线程获取的锁,那么您将陷入死锁。

    终结器线程死锁的例子比比皆是。这是一篇有关这种情况的好文章,其中包含许多其他情况的链接:

    http://blogs.microsoft.co.il/blogs/sasha/archive/2010/06/30/sta-objects-and-the-finalizer-thread-tale-of-a-deadlock.aspx

    如文章所述:终结器是一种极其复杂且危险的清除机制,如果可以,请避免使用。错误地确定终结器非常容易,而正确定则非常困难。

    关于c# - 垃圾收集和终结者:更细的分数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5223325/

    10-14 04:13