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

时间:2012-04-06 13:16:50

标签: c# .net garbage-collection clr finalizer

 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

finilizer 执行,因为我来自CriticalFinalizerObject

我不想在Finalizer中使用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();
        }
    }

3 个答案:

答案 0 :(得分:11)

正如文件明确指出:

  

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

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

更新:

您已经更新了代码,试图使其遵循约束执行区域的规则,但我没有看到您已正确完成的任何证据。规则很清楚;约束执行区域中的终结器绝对不能执行以下任何操作:

  • 分配内存
  • 框一个值类型
  • 获得锁定
  • 调用任意虚方法
  • 调用任何缺乏可靠性合同的方法

你的终结者会做这五件事吗?如果确实如此,则不要求CLR满足您对终结器始终运行的期望。

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

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

答案 1 :(得分:10)

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

您可以阅读更多here

  

在对象变得不可访问之后,将自动调用此方法,除非通过调用SuppressFinalize来豁免对象的最终化。在关闭应用程序域期间,会自动调用Finalize,这些对象不会被终止,即使是那些仍然可访问的对象。 Finalize在给定实例上仅自动调用一次,除非使用ReRegisterForFinalize和GC等机制重新注册该对象。随后未调用GC.uppressFinalize。

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

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

更新:(为什么不调用关闭功能?) 实际上你调用了Close函数(如果在其他终结器中没有例外)。如果你保持默认的TraceListener(删除Trace.Listeners.Clear()调用),你会看到所有字符串都成功写入Output窗口。

问题在于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);

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

答案 2 :(得分:0)

在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