【问题标题】:C# Will the typical IDisposable pattern not cause resource leak in some conditionC# 在某些情况下,典型的 IDisposable 模式是否不会导致资源泄漏
【发布时间】:2021-01-17 13:08:24
【问题描述】:

下面是 C# 类的典型 IDisposable 实现,它同时具有托管和非托管资源。我的问题是,是否存在在任何给定对象的类的 Dispose() 方法之前调用 ~DisposableObject() 析构函数方法的情况。如果可能的话,它不会将标志设置为false,并且托管资源将永远不会有机会被释放,因为如果将dispose标志设置为false,Dispose(bool)方法不会做任何事情。

public class DisposableObject : IDisposable
    {
        private bool disposed;
        private ManagedResource mgdRs;
        private UnmanagedResource unMgdRs;

        public DisposableObject()
        {
            mgdRs = new ManagedResource();
            unMgdRs = new UnmanagedResource();
        }

        ~DisposableObject()
        {
            this.Dispose(false);
        }
    
        public void Dispose()
        {    
             this.Dispose(true);
             GC.SuppressFinalize(this);       
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    // clean up managed resources
                    //call dispose on your member objects
                    mgdRs.Dispose();
                }

                // clean up unmanaged resources
                unMgdRs.Destroy();
                this.disposed = true;
            }
        }
    }
}

【问题讨论】:

  • docs.microsoft.com/en-us/dotnet/standard/garbage-collection/… 我发现你有点难以理解 ms 示例,一个更容易一些.. 我认为它们不完全相同。当我关注 ms 时,它会更容易,并且努力查看您是否有问题是有意义的。基本上如果它已经被调用然后处理。
  • 您在问终结器是否可以在调用 Dispose() 之前运行?那么是的,因为程序创建一次性对象然后从不对其调用Dispose() 是合法的:语言中没有任何内容需要 Dispose() 被调用。如果ManagedResource 有自己的终结器,那么终结器可能DisposableObject 的终结器运行时已经运行。你真的无能为力,因为finalizers might not ever run anyway
  • 析构函数可以在任何时候被调用......甚至比你想象的要早
  • 修改文档相关讨论@Seabizkit 链接:The Dispose guidelines really should be updated to point developers in the right direction。很长,但你必须通读。
  • 我发现官方指导非常混乱,并且总是参考 Stephen Cleary 的建议(IDisposable 上的Stephen's blogthis codeproject article。在这种情况下:重构以便您的类只处理托管或非托管资源,然后按照链接文章中的指南进行操作。

标签: c# idisposable finalize


【解决方案1】:

是的,可能首先调用终结器。但是,这里需要了解三件事。

首先,在 C# 中很少需要实现完整的 IDisposable 模式。仅当您同时拥有同一类型的托管资源和原始非托管资源时,才需要完整模式。

您处理的大多数非托管资源已经有一个您依赖的托管包装器,因此无需实现另一个终结器/析构器。此外,实现需要终结器的原始非托管资源的开发人员通常会将自己限制为仅使用一个资源,这样就没有相同类型的托管资源来创建问题的冲突。通过这种方式,非托管资源变为托管资源,现在可以作为更简单模式的一部分包含在内。

要了解的第二件事是终结器作为垃圾收集的一部分运行。鉴于终结器正在运行,我们知道非托管资源已被清理,只剩下托管资源。托管资源的定义是垃圾收集器将采用的东西。因此,如果终结器运行得太快,导致Dispose() 调用错过了托管资源,那没关系,因为垃圾收集器已经在运行并且会处理它。

这让我想到了我的第三件事。不仅可以首先调用终结器,而且当这种情况发生时,很可能只调用终结器,因为除了垃圾收集器之外的任何东西调用终结器都是非常不寻常的。如果垃圾收集器正在为一个对象运行,则不会有任何剩余的引用可用于也调用 Dispose(),无论是有意还是隐含。同样,没关系:终结器正在运行,因此我们知道非托管资源将被清理,垃圾收集器正在运行,因此我们知道托管资源也将被清理。

【讨论】:

  • 我很确定这是不正确的。垃圾收集器不会像您在上一条评论中建议的那样收集非托管资源。如果是这样,那么它就不会是不受管理的。 #tag
  • “是的,有可能(虽然很少)首先调用终结器。”这怎么可能?有记录的案例吗?我不认为终结器可以在Dispose() 之前随时执行。调用 dispose 需要硬引用。当然,我不是在谈论对象复活案例和僵尸对象 - 在这种情况下显然是可能的。
  • @Adassko var f = new FileStream(path); 几乎可以保证终结器将在Dispose 之前执行(可能永远不会被调用,因为不使用using 也不太可能处理对象)。
  • @AnandSrivastava 您的情况是否真的涉及到原始非托管资源?这非常罕见
  • @Alexei:啊……我明白你对这个问题的理解与我的不同。 FWIW,基于 “因为 Dispose(bool) 方法在处理标志设置为 false 时不会做任何事情”,我认为 OP 实际上是 具体询问实际调用的 Dispose() 方法(但在终结器之后),而不是关于在没有首先调用 Dispose() 的情况下调用终结器的更一般情况(无论如何都不应该发生,因为正确的 Dispose()会打电话给SuppressFinalize()),也不会被打电话。
猜你喜欢
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
  • 2011-05-24
相关资源
最近更新 更多