为什么我的 Close 函数没有被调用

本文关键字:调用 函数 我的 Close 为什么 | 更新日期: 2023-09-27 18:30:21

 class Program : CriticalFinalizerObject
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            TextWriterTraceListener listener = new TextWriterTraceListener(@"C:'trace.txt");
            Trace.Listeners.Clear(); // Remove default trace listener
            Trace.Listeners.Add(listener);
            Trace.WriteLine("First Trace"); // Generate some trace messages
            Trace.WriteLine("Perhaps last Trace.");
        }
        ~Program()
        {
            Trace.Close();
        }
    }

我得到文件大小 =0

Finiizer应该执行,因为我从CriticalFinalizerObject

我不想在终结器中使用 Trace.Close()。

编辑

@eric Lippert回复后:我重新编辑了代码,试图将其与:约束执行区域匹配(但仍然没有成功)

  [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    class Program : CriticalFinalizerObject
    {
        static void Main(string[] args)
        {
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
            }
            catch (Exception e)
            {
            }
            finally
            {
                Program p = new Program();
                TextWriterTraceListener listener = new TextWriterTraceListener(@"C:'trace1.txt");
                Trace.Listeners.Clear();
                Trace.Listeners.Add(listener);
                Trace.WriteLine("First Trace");
                Trace.WriteLine("Perhaps last Trace.");
            }
        }
        ~Program()
        {
            Trace.Flush();
        }
    }

为什么我的 Close 函数没有被调用

正如文档明确指出的那样:

在派生自 CriticalFinalizerObject 类的类中,公共语言运行库 (CLR) 保证所有关键终结代码都有机会执行,前提是终结器遵循 CER 的规则,即使在 CLR 强制卸载应用程序域或中止线程的情况下也是如此。如果终结器违反了 CER 的规则,则可能无法成功执行

终结器是否遵循约束执行区域的所有规则?

更新:

已经更新了代码,试图使其遵循受约束的执行区域的规则,但我看不到任何证据表明您这样做是正确的。规则非常明确;约束执行区域中的终结器绝对不能执行以下任何操作:

  • 分配内存
  • 框 A 值类型
  • 获取锁
  • 调用任意虚拟方法
  • 调用任何缺少可靠性协定的方法

您的终结器是否执行这五项操作中的任何一项?如果是这样,则 CLR 不需要满足您希望终结器始终运行的愿望。

此外:暂时忘记受约束的执行区域,因为您现在的程序甚至不是线程安全的。您已将令人讨厌的竞争条件写入程序。

在跟踪开始之前,是什么阻止了垃圾收集p的抖动?无!抖动知道 p 永远不会再被使用,并且完全有权在分配后立即收集它。刷新可能随时发生在终结器线程上,包括在跟踪写入发生之前,或者在任何跟踪写入的中间。

因为您没有创建程序类的实例。

您可以在此处阅读更多内容:

在对象变得不可访问后,将自动调用此方法,除非该对象已通过调用 SuppressFinalize 从完成中免除。在应用程序域关闭期间,将自动对未免除完成的对象调用 Finalize,即使是那些仍可访问的对象。Finalize 仅在给定实例上自动调用一次,除非使用 ReRegisterForFinalize 和 GC 等机制重新注册对象。SuppressFinalize 随后未被调用。

因此,如果要调用 finilizer,则需要具有对象的实例。

更新:如果要写入消息,请考虑使用Trace.AutoFlush = true;

更新:(为什么不调用关闭函数?实际上,您调用了Close函数(如果其他终结器中没有发生任何异常)。如果保留默认的 TraceListener(删除Trace.Listeners.Clear()调用),你将看到所有字符串都已成功写入"输出"窗口。

这里的问题是StreamWriter(在TextWriterTraceListener中创建)没有终结器。因此,它不会将所有数据刷新到文件。您需要做什么:

FileStream file = new FileStream(@"C:'trace.txt", FileMode.OpenOrCreate);
StreamWriter writer = new StreamWriter(file);
GC.SuppressFinalize(file);
GC.SuppressFinalize(file.SafeFileHandle);
var listener = new TextWriterTraceListener(writer);

实际上,您需要在终结器上手动关闭文件。

在 C# 中,您无法确定何时或是否调用终结器,因为 GC 会处理该命令。因此,如果你有一些资源要发布,最好创建一个实现IDisposable的类,并像这样使用它:

using (MyResourceHolder rh = new MyResourceHolder()) {
    // ...
} // rh.Dispose() is called implicitly

在 Dispose 方法中,您可以调用 Trace.Close()

有关IDisposable的良好实现,请参阅 http://msdn.microsoft.com/en-us/library/system.idisposable.aspx。