查看IDisposable模式+ Finalizer模式,有些事我不明白:
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()怎么办?在那种情况下,终结者应该开始,对吗?但处理(假);什么都不做(只设置dispos = true)。这是有意的吗?感觉好像缺少某些东西......
答案 0 :(得分:0)
问题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
}
答案 1 :(得分:0)
如果Dispose(false)不会做任何事情,那么这是一个非常好的迹象,表明你的类或从它派生的任何类都不应该包含C#-style“析构函数”,也不能覆盖Finalize和“disposing”参数应被视为一个虚拟人,其目的是为受保护的Dispose方法提供与公共方法不同的签名。
请注意,当父类不期望这样的行为时,在派生类中实现析构函数或重写Finalize可以生成Heisenbugs。除其他事项外,GC有时可以决定放弃一个类对象,触发其终结器/析构函数,即使正在使用该类字段引用的实体时也是如此。例如,假设静态类usbThingie
使用整数句柄操作USB控制器,而包装类usbWrapper
执行如下操作:
UInt32 myHandle; void sendData(Byte data[]) { UsbThingie.send(myHandle, data[0], data.Length); }
如果sendData()调用是usbWrapper
实例在放弃之前做的最后一件事,那么垃圾收集器可能会在调用UsbThingie.send()后观察它 - 甚至在它返回之前 - usbWrapper
不存在进一步的引用,因此它可以安全地触发终结器。如果终结器试图关闭myHandle
引用的通道,则可能会破坏正在发生的传输;如果usbThingie不是线程安全的,那么就不知道会发生什么。