这是一个我不确定的例子:
public class SomeClass : IDisposable {
~SomeClass() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
private bool _disposed;
protected virtual void Dispose(bool disposing) {
if (!_disposed) {
if (disposing) {
// TODO: Release any managed resources here...
}
// ?! Is it safe to enumerate the dictionary here ?!
foreach (var resource in _resources.Values)
ReleaseBuffer(resource);
_resources = null;
_disposed = true;
}
}
private Dictionary<string, IntPtr> _resources;
...
}
枚举托管字典以释放非托管资源是否安全?
字典的可用性是否不确定,因为未定义调用终结器的顺序?
以下是从MSDN中引用的引用,我觉得令人困惑[1]:
- 两个对象的终结器不保证以任何特定顺序运行,即使一个对象引用另一个对象。也就是说,如果对象A具有对象B的引用并且两者都具有终结器,则当对象A的终结器开始时,对象B可能已经完成。
答案 0 :(得分:2)
我建议使用独立包装器对象的字典,而不是拥有非托管资源的字典,每个包装对象都负责保护一个非托管资源。如果放弃了保存字典的对象并且包装器对象不存在其他引用,则所有包装器对象将被最终化,而不需要在该过程中涉及字典本身。使用这种方法将更容易理智地处理在对象构造期间发生异常的情况,并且至少在某种程度上处理对象在其被排队以用于最终化和终结器的时间之间复活的情况。运行[代码通常不能运行&#34;正确&#34;在这种情况下,但应避免破坏系统其余部分的状态]。
例如,使用句柄的代码可能会在使用过程中获取锁定,并在使用后检查&#34; disposeObjectASAP&#34;旗;如果设置,则重新获取锁定并处置对象。终结器本身应该设置标志,然后尝试获取锁;如果它成功获取锁,它应该处置该对象。如果不能,它设置标志的事实应该意味着具有锁定的代码注定要检查标志并清理对象,因此终结器不必。如果终结器过早运行,它可能释放另一个线程将需要的资源,导致其他线程上的操作失败,但终结器不会释放资源而另一个线程正在使用它们或处理它们,因为释放资源在这些情况下可能会导致大规模的系统损坏。
答案 1 :(得分:1)
根据Implementing a Dispose method,您在此处显示的代码不安全。
代码示例显示:
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
}
您的示例显示您在条件块中释放非托管资源。 MSDN示例显示您应该释放条件块中的托管资源。
正如文中所述(标题下,“Dispose(布尔值)重载”):
如果方法调用来自终结器(即,如果处置为
false
),则只执行释放非托管资源的代码。由于未定义垃圾收集器在最终确定期间销毁托管对象的顺序,因此使用值Dispose
调用此false
重载会阻止终结器尝试释放可能已经回收的托管资源。