是不是保证完成运行的析构函数?

时间:2012-03-30 11:01:01

标签: c# destructor finalizer

析构函数很奇怪。我试图通过使用“智能”参考管理来消除使用一次性模式的需要,确保垃圾收集器可以在正确的时间收集对象。在我的一个析构函数中,我不得不等待来自另一个对象的事件,我注意到它没有。应用程序只是关闭,析构函数在执行过程中被终止。 我希望总是允许析构函数完成运行,但是下面的测试表明这不是真的。

using System;
using System.Diagnostics;
using System.Threading;


namespace DestructorTest
{
    class Program
    {
        static void Main( string[] args )
        {
            new DestructorTest();
            new LoopDestructorTest();
            using ( new DisposableTest() ) { }
        }
    }

    class DestructorTest
    {
        ~DestructorTest()
        {
            // This isn't allowed to finish.
            Thread.Sleep( 10000 );
        }       
    }

    class LoopDestructorTest
    {
        ~LoopDestructorTest()
        {           
            int cur = 0;
            for ( int i = 0; i < int.MaxValue; ++i )
            {
                cur = i;
            }
            // This isn't allowed to finish.
            Debug.WriteLine( cur );
        }
    }

    class DisposableTest : IDisposable
    {
        public void Dispose()
        {
            // This of course, is allowed to finish.
            Thread.Sleep( 10000 );
        }
    }
}

那么,是不是保证完成运行的析构函数?

4 个答案:

答案 0 :(得分:16)

  

那么,是不是保证完成运行的析构函数?

没有。根据我的记忆,当进程终止时,它会给终结器几秒钟的执行时间,然后突然终止进程。你不会想要一个糟糕的终结器来阻止一个进程完成,是吗?

您应该将最终确定视为“尽力而为”的清理 - 特别是,在整个系统突然关闭的情况下,例如BSOD或断电, 不会发生

编辑:我发现了一些blog post from Joe Duffy

形式的伪文档
  

如果在停止所有正在运行的线程的过程中锁被孤立,则关闭代码路径将无法获取锁。如果这些采集是在非超时(或长时间超时)获取的情况下完成的,则会发生挂起。为了解决这个问题(以及可能发生的任何其他类型的挂起),CLR会发出一个看门狗线程来监视终结器线程。虽然可配置,但默认情况下CLR会让终结器在变得不耐烦之前运行2秒;如果超过此超时,终结器线程将停止,并且继续关闭而不会耗尽终结器队列的其余部分。

答案 1 :(得分:1)

  

那么,是不是保证完成运行的析构函数?

虽然它不在您的代码中,但可能存在从Dispose()方法明确调用GC.SuppressFinalize的情况。这压制了最终化,并且适用于不需要它的对象。

这可以显着提高性能,因为可终结对象将始终存在于一个垃圾收集中,即它将被提升为gen1甚至gen2 GC,其具有与之相关的更高成本。

答案 2 :(得分:1)

.NET没有析构函数。您的代码包含终结器

当对象被垃圾收集时调用终结器,而不是在它被取消时调用。它们的执行时间也有限,以防止悬挂物体。

另见https://en.wikipedia.org/wiki/Finalizer

答案 3 :(得分:0)

另一种方法是在垃圾收集器释放内存时调用终结器。

但是,如果你的程序最多需要1Mb的内存,但是在一台内存为10Mb的机器上运行,那么grabage collector的有效实现就是什么都不做(因为程序有足够的内存来运行执行)。在这种情况下,永远不会召唤终结者。