C# 不是';t析构函数保证完成运行?

C# 不是';t析构函数保证完成运行?,c#,destructor,finalizer,C#,Destructor,Finalizer,析构函数很奇怪。我试图通过使用“智能”引用管理消除使用一次性模式的需要,确保垃圾收集器能够在正确的时间收集对象。在我的一个析构函数中,我必须等待来自另一个对象的事件,但我注意到它没有。应用程序简单地关闭,析构函数在执行过程中终止。 我希望始终允许析构函数完成运行,但正如下面的测试所表明的,这是不正确的 using System; using System.Diagnostics; using System.Threading; namespace DestructorTest { c

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

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 );
        }
    }
}
使用系统;
使用系统诊断;
使用系统线程;
命名空间析构函数测试
{
班级计划
{
静态void Main(字符串[]参数)
{
新的析构函数test();
新的LoopDestructorTest();
使用(新的DisposableTest()){}
}
}
类析构函数测试
{
~DestructorTest()
{
//这是不允许完成的。
睡眠(10000);
}       
}
类LoopDestructorTest
{
~LoopDestructorTest()
{           
int cur=0;
对于(int i=0;i
那么,析构函数不保证完成运行吗?

那么,析构函数不保证完成运行吗

不。据我所记得的,当进程终止时,它会给终结器几秒钟的时间来执行,但随后会突然终止进程。你不会希望一个糟糕的终结器阻止一个进程完成,是吗

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

编辑:我发现了一些伪文档,其形式为:

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

那么,析构函数不保证完成运行吗

虽然它不在您的代码中,但在某些实例中,可以从
Dispose()
方法显式调用。这会抑制终结,并且适用于不需要终结的对象


这可以显著提高性能,因为可终结对象将始终在一次垃圾收集中存活,即它将被提升到gen1甚至gen2 GC,这会带来更大的相关成本

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

当对象被垃圾收集时调用终结器,而不是当对象为空时调用终结器。它们还可以获得有限的执行时间,以防止挂起对象


另请参见。

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


但是,如果您有一个程序,它最多需要1Mb内存,但运行在一台有10Mb内存的机器上,那么抓取收集器的有效实现就是什么也不做(因为有足够的内存供程序执行)。在这种情况下,将不会调用终结器。

是否等待终结器中另一个对象的事件?这太疯狂了!你真的应该坚持使用IDisposable模式来进行可靠的销毁和资源释放。@MattDavey。。。等待来自另一个对象的事件继续触发
Dispose()
不是疯狂吗?有什么区别?阅读
下的第二个项目符号,Finalize方法可能无法运行完成,或者在以下异常情况下可能根本无法运行:
事实上我会,或者至少我希望在Visual Studio中得到一个巨大的警告,警告应用程序没有正确完成(因为并非所有析构函数都被允许完成运行)@StevenJeuris:听起来你在一个你真的不应该依赖终结器的情况下依赖终结器。终结器应该有助于避免资源泄漏-如果您的系统没有运行到完成状态,那么您应该重新设计。毕竟,由于BSOD、断电等原因,它们始终有可能无法运行。当然,重新设计涉及使用
IDisposable
,它不会突然终止,因为它必须等待x ms,等待一个事件,该事件指示处置步骤已完成,并且可以执行下一步。我只是不明白析构函数的意义。在什么情况下你应该依赖它们?@StevenJeuris:依赖它们始终执行,以确保在进程之外的持久状态下的正确性?就我而言,从来没有。你应该总是认为你的系统可能只是停止执行。您可以依靠它们在一个过程中清理不需要急于释放的资源。就我个人而言,我不记得上次我写终结器是什么时候了……系统故障对我来说不重要,持久性也不重要。我只是尝试清理非托管内存,而不必担心何时(例如,使用
或调用
Dispose()
)。您的答案清楚地说明了为什么必须通过