在终结器中处理IDisposables列表

时间:2012-08-27 13:05:22

标签: c# garbage-collection unmanaged idisposable finalizer

我有几个非托管内存结构用于与c ++ dll通信。 每个这样的结构都必须手动释放,因此我将其包装在实现MyUnmanagedStructure的{​​{1}}中。

我总是需要可变数量的这些结构,所以我有一个集合IDisposable,它也实现了IDisposable。

(请参阅下面的最小示例代码)

只要我的库的用户总是调用Dispose()或用MyUnmanagedStructureCollection包装集合就没有问题,但我无法保证。即使用户没有手动处理集合,我也不想泄漏内存。

当垃圾收集通过终结器调用using() {}方法时,据我所知,我不能确定我的MyUnmanagedStructureCollection.Dispose()还没有被垃圾收集,所以如何处理在这种情况下每个结构?

在我的最终化代码中,我是否应该尝试迭代列表,希望它还没有被垃圾回收?

在try / catch块中执行此操作,捕获ObjectDisposedException是不错的做法?

或者我应该让每个unmanagedStructure“自生自灭”,依靠个人终结者,在我的收藏品的终结者中什么都不做?

private List<MyUnmanagedStructure>

2 个答案:

答案 0 :(得分:2)

GC的工作方式是:

  1. 查找所有可到达的对象
  2. 将具有终结器的所有无法到达的对象排入结束队列
  3. 将从终结队列中可到达的所有对象标记为可到达
  4. 释放剩余的无法访问的对象
  5. 从正在运行终结器的对象引用的对象尚未被垃圾回收。

    您唯一需要注意的是,最终确定的顺序是未定义的。因此,列表的元素可能尚未最终确定,但未收集。最终确保是单线程的,所以你也需要锁定。

    人们通常会试图避免这种终结链,因为独立定型更简单。但是如果某些物体需要在其他物体之前处置,那么这种结构是不可避免的。

    您还应该使用SafeHandle来考虑关键定稿。


      

    <强>可达性

         

    最终确定的指导原则之一是Finalize方法不应该触及其他对象。人们有时会错误地认为这是因为已经收集了其他对象。然而,正如我所解释的那样,促进了可终结对象的整个可到达图形。

         

    指南的真正原因是避免触及可能已经完成的对象。那是因为最终确定是无序的。

    Chris Brumme on finalization

答案 1 :(得分:1)

由于您MyUnmanagedBuffer类是MyUnmanagedStructureCollection类的观点的托管资源,我认为它应该处理它。这意味着MyUnmanagedStructureCollection.Dispose(bool)方法将如下所示。

protected virtual void Dispose(bool disposing) {
    if (!disposed) {
        // Dispose unmanaged resources 
        // Should not access managed resources, 
        // the garbage collection may have claimed them already!

        if (disposing) {
            // Dispose managed resources.
            // This means that we try to dispose all items in the structures collection.
            if (this.structures != null) {
                foreach (var structure in this.structures) {
                    structure.Dispose(disposing);
                    this.removeAllMemoryPressure(); // What does this?
                }
                this.structures.Clear();
                this.structures = null;
            }
        }
    }

    disposed = true;
}