我正在开发一个非常广泛的.NET系统,它涉及很多系统编程。大部分时间,我使用IDisposable模式处理资源处理,但有时这不适用(或错误地遗漏)并且在Finalize()期间资源被破坏。这可能发生在COM互操作中,或者当析构函数调用Dispose()并且内部存在异常时。
基本上:当终结者可能抛出时,并不总是能够清楚地看到并处理每个场景。当它发生时,应用程序肯定会崩溃。
我特别关注这类问题的原因是终结器不是由创建或使用对象的线程调用的,因此将异常绑定到对象所在的上下文几乎是不可能的。创建。你得到的只是一些通用的GC线程。
所以,现在向公众提问:如果你考虑到这些问题,你会怎么做才能控制它们?标记对象?使用第三方工具来跟踪这些问题吗?
另外:是否有可能触发某种全局“终结者投掷”事件,至少记录这个问题已经发生?
EDIT1 :非常感谢所有提交宝贵意见的人,我想我现在对需要做些什么有点清楚了。我真的想从这个讨论得到的最后一件事是,如果有人知道在终结器中触发异常代码的方法(即使应用程序崩溃仍然是不可避免的),所以我至少可以记录它,而不必修改每个的析构函数类。
答案 0 :(得分:5)
你的问题是基于一个有点错误的前提,即不可能处理终结者可能抛出的每个场景。这正是您在此需要实现的目标。在最终确定期间发生的例外将终止该过程。除非异常真的是致命的,在这种情况下让程序崩溃,你应该以不会导致程序崩溃的方式处理这个异常。
使用Finalizer抛出几乎与使用C ++析构函数抛出一样糟糕。 IDisposable的大多数实现都为活动(IDisposable.Dispose())和被动(终结器线程)部署操作调用相同的方法。如果Finalizer版本正在抛出,那么主动部署可能或可能也会抛出。这与C ++析构函数抛出非常相似,可以防止嵌套资源在某些情况下被正确处理。
不允许异常从终结器传播,除非它们对您的应用程序真正致命。
答案 1 :(得分:4)
对于直接拥有非托管资源(即不仅仅通过IDisposable成员)的类,只需在.NET中使用终结器。这些类必须使用MSDN中描述的标准模式实现IDisposable。
终结器应该只处理非托管资源 - 通常的模式是调用一个方法“protected void Dispose(bool disposing)”,disposing参数设置为false。
此方法通常实现如下:
protected void Dispose(bool disposing)
{
if (disposing)
{
// Dispose managed resources here.
// (e.g. members that implement IDisposable)
// This could throw an exception, but will *not* be called from the finalizer
...
}
... Dispose unmanaged resources here.
... no need for any exception handling.
... Unlikely to get an exception, and if you do it will be fatal.
}
由于您只处理非托管资源,因此通常不应引用任何托管对象,也不应包含任何异常处理。
答案 2 :(得分:3)
我在我的课程中执行以下操作,因此在调试版本中,我可以尝试捕获这些类型的东西。
在调试版本中,执行断言以确保明确处理该类。另一个断言是为了确保终结器不会抛出。在发布/生产版本中,不会执行此检查以提高性能。
using System;
using System.Diagnostics;
namespace ConsoleApplication8
{
class Program
{
class IReferenceUnmanagedMem : IDisposable
{
#if(DEBUG)
private static readonly string _ctorStackTrace = Environment.StackTrace;
#endif
public IReferenceUnmanagedMem()
{
}
~IReferenceUnmanagedMem()
{
#if(DEBUG)
Debug.Fail("Dispose method not called.", _ctorStackTrace);
try
{
#endif
Dispose(true);
#if(DEBUG)
}
catch(Exception e)
{
Debug.Fail("Dispose method threw exception in finalizer.", e.ToString());
}
#endif
}
public void Dispose()
{
Dispose(false);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool inFinalizer)
{
if(inFinalizer)
{
throw new Exception("I Know, this is a no-no.");
}
}
}
public static void Main()
{
IDisposable disposable = new IReferenceUnmanagedMem();
disposable = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
答案 3 :(得分:0)
Apache DBCP在处理废弃连接时,似乎在打开连接时会生成异常,以便在清理连接时存储堆栈跟踪并将其吐出。对于未正确处理的资源,您可以尝试使用这样的技术来跟踪资源的初始化和使用方式,以便finalize方法可以产生有用的错误输出。