如果程序意外退出(异常或进程终止)会发生什么?是否存在此类(或其他)程序将终止的情况,但IDisposable
对象将无法妥善处理?
我问的原因是因为我正在编写与外围设备通信的代码,我想确保它不会被置于糟糕的状态.
如果原因是异常并从using
块或try catch finally
块中抛出,则将按原样处理.如果它没有被using
块捕获,则它不会自动处理(就像应用程序正常关闭时不会那样).
一个样品:
IDisposable d1 = new X(); using (IDisposable d2 = new X()) { throw new NotImplementedException(); } d1.Dispose();
d1
没有处理,d2
通常是.某些类型的异常可能会阻止using
块的处理以及某些程序崩溃.如果原因是电源故障或系统崩溃,当然您无能为力.
如果程序意外退出(例如,您终止进程),则绝对无法保证IDisposable.Dispose
将调用该方法.你最好不要依赖它来做这类事件.必须通过代码手动调用Dispose方法,这不是CLR会自动为您调用的内容.
除了Patrick Hofman和Alexei的答案之外,即使应用程序正确终止,也可能无法执行清理.
您可能知道,Dispose
当垃圾收集器收集实现IDisposable
接口的对象时,不会调用该方法.但GC会调用该Finalize
方法也称为终结器.在其中,您应该使用Dispose Pattern编写清理逻辑.是的,.Net Framework将尝试运行所有终结器,但没有保证它们会被执行.
例如,下面的程序有很长的终结器.因此,.Net将终止该过程,您将永远不会看到该消息.
class FinalizableObject { ~FinalizableObject() { Thread.Sleep(50000); Console.WriteLine("Finalized"); } } class Program { static void Main(string[] args) { new FinalizableObject(); } }
这可能是由任何长时间运行的操作引起的,例如释放网络句柄或其他需要很多时间的操作.
因此,您不应该依赖终结器和一次性物体.但是所有打开的内核对象句柄都会自动关闭,所以你不必担心它们.
除了答案之外,我建议你阅读一些关于终结器和GC的有趣文章:
每个人都以错误的方式思考垃圾收集(Raymond Chen)
当你知道的一切都是错的时候,第一部分(Eric Lippert)
当你知道的一切都是错的时候,第二部分(Eric Lippert)
终止进程(MSDN)
使用控制台应用程序进行的一个非常简单的测试表明,在进程终止时不调用Dispose:
class DisposableTest : IDisposable { public void Dispose() { Console.WriteLine("Dispose called"); } } ... using (DisposableTest sw = new DisposableTest()) { Thread.Sleep(20000); }
使用任务管理器终止进程不会触发Disposable.Dispose()
方法.等待20秒钟.
因此,如前所述,当应用程序崩溃或被杀死时,不要依赖于一次性对象.但是,异常应该触发它.我只是想知道是否异常,例如StackOverflowException
或OutOfMemoryException
将始终触发Dispose().
[编辑]
刚刚测试了我的好奇心:
StackOverflowException
获取进程终止,因此不调用Dispose()
OutOfMemoryException
允许正常调用Dispose()