IDisposable接口+终结模式
望着IDisposable模式+终结模式,有件事我不明白:IDisposable接口+终结模式
public class ComplexCleanupBase : IDisposable
{
private bool disposed = false; // to detect redundant calls
public ComplexCleanupBase()
{
// allocate resources
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// dispose-only, i.e. non-finalizable logic
}
// shared cleanup logic
disposed = true;
}
}
~ComplexCleanupBase()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
从我的理解的模式应该像上面实现。
1)调用Dispose()触发GC.SuppressFinalize(this),这意味着该对象不应该放在终结器队列上,因为它已经正确处理了?这有助于更快地释放对象?
2)但是如果我根本就不在这个对象上调用Dispose()怎么办?在这种情况下,终结者应该踢进,对吗?但是Dispose(false);完全没有(只设置处置=真)。这是打算?感觉好像有什么东西丢失......
问题1:是的,如果GC.SuppressFinalize没有被调用,对象将被放置在终结器队列中并且将向上移动一代(如果不是已经是第2代的),这意味着该对象的内存直到新一代GC的下一次传递才会被回收。
问题2:您的评论//shared cleanup logic
是共享清理逻辑将去的地方,这是设置disposed = true
以外的事情会发生的地方。
另外,顺便说一句:如果你的处置逻辑应该只被调用一次,考虑收购一个lock
,无争议的锁是非常快的。Net:
public class ComplexCleanupBase : IDisposable
{
private volatile bool disposed = false; // to detect redundant calls
private readonly object _mutex = new object();
protected virtual void Dispose(bool disposing)
{
if (!Monitor.TryEnter(_mutex))
{
// We're already being disposed.
return;
}
try
{
if (!disposed)
{
if (disposing)
{
// dispose-only, i.e. non-finalizable logic
}
// shared cleanup logic
disposed = true;
}
}
finally
{
Monitor.Exit(_mutex);
}
}
... other methods unchanged
}
如果的Dispose(假)是不会做任何事情,这是一个非常好的迹象表明,你的类或从它派生的任何类都不应该包含C#风格的“析构函数”,也不会覆盖Finalize,而“disposing”参数应该被视为一个虚拟元素,其目的是为受保护的Dispose方法与公共签名不同。
请注意,当父类不期望这种行为时,在派生类中实现析构函数或重写Finalize可以生成Heisenbugs。除此之外,GC有时可能会决定放弃一个类对象,甚至在正在使用该类的某个字段引用的实体时触发它的终结器/析构函数。例如,假设一个静态类usbThingie
使用整数手柄操纵USB控制器和包装类usbWrapper
确实是这样的:
UInt32 myHandle; void sendData(Byte data[]) { UsbThingie.send(myHandle, data[0], data.Length); }
如果送出数据()调用做的usbWrapper
实例的最后一件事之前,它是被废弃的时候,垃圾收集器可能会观察到一旦UsbThingie.send()被调用 - 甚至在它返回之前 - usbWrapper
将不再存在引用,因此它可以安全地触发终结器。如果终结器尝试关闭myHandle
所提及的通道,则可能会中断正在发生的传输;如果usbThingie不是线程安全的,那么就不知道会发生什么。
终结器不应该阻止获取锁,即使锁预计不会持续很长时间。如果某些事情导致锁定不当,可能会阻止任何其他终结器运行。 – supercat 2014-01-20 00:40:38
@supercat在这种情况下,呼叫在终结者中不可能被阻止。 '_mutex'只能用于处理目的。一般来说,你是正确的,但在这种情况下,你的观点(应该是)是没有意义的。 – 2014-01-20 10:12:20
@supercat只是想到了一种情况,使我以前的评论不正确:如果在另一个实例的终结过程中,它会将ComplexCleanupBase的实例恢复生命,以便它在应用程序中具有另一个根,那么可能会在互斥体上争用,尽管这应该*真的*不会完成。答案更新完全一样。 – 2014-01-20 12:58:30