10
 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

完成程序应该已经执行,因为我来自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();
        }
    }
4

3 回答 3

11

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

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

您的终结器是否遵循受限执行区域的所有规则?

更新:

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

  • 分配内存
  • 框一个值类型
  • 获取锁
  • 调用任意虚方法
  • 调用任何缺乏可靠性契约的方法

你的终结者会做这五件事中的任何一件吗?如果是这样,则不要求 CLR 满足您对终结器始终运行的愿望。

此外:暂时忘记受约束的执行区域,因为您现在的程序甚至不是线程安全的。你在程序中写了一个讨厌的竞争条件。

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

于 2012-04-06T13:53:50.023 回答
10

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

你可以在这里阅读更多:

此方法在对象变得不可访问后自动调用,除非该对象已通过调用 SuppressFinalize 免除终结。在应用程序域关闭期间,会自动对未免于完成的对象调用 Finalize,即使是那些仍可访问的对象。Finalize 只在给定实例上自动调用一次,除非该对象使用 ReRegisterForFinalize 等机制重新注册,并且随后没有调用 GC.SuppressFinalize。

所以,如果你想调用finilizer,你需要有对象的实例。

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

更新:(为什么不调用 Close 函数?)实际上你调用了 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);

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

于 2012-04-06T13:18:27.290 回答
0

在 C# 中,您无法确定何时或您的终结器被调用,因为 GC 会处理它。因此,如果您有一些资源要释放,最好创建一个类来实现IDisposable和使用它,如下所示:

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

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

有关. _ _IDisposable

于 2012-04-06T13:38:44.150 回答