【问题标题】:Disposing a Bitmap through its Finalizer通过其终结器处理位图
【发布时间】:2010-05-13 14:18:46
【问题描述】:

我有一个复杂的程序,我必须首先在其中创建,然后使用位图包装器并将它们发送到许多不同的类中。最后的问题是决定哪些类应该处理位图。大多数时候,最终类不知道他们是否确实可以处理位图,因为同一个位图可以在多个地方使用。另外,我不能只复制位图,因为这是一种资源密集型算法,这样做会很慢。

我查看了 Image/Bitmap 实现的反射器,它们似乎使用了 Dispose Pattern。因此,即使我不调用 Dispose(),CLR 最终也会在其他时候调用它。

如果我只是让位图保持原样,让终结器处理它们,那会不会太糟糕了?

【问题讨论】:

  • 您能否针对这些图像再描述一下您的应用程序的生命周期?是否有一个特定点知道应该处理所有(或部分)图像?

标签: c# .net vb.net dispose idisposable


【解决方案1】:

您的主要问题似乎是您的设计,但现在修复可能为时已晚和/或太昂贵。但总的来说,避免持有资源太久。

由于您正在使用包装器,您可以将reference counting 视为一种设计模式。任何持有对象都可以注册和取消注册其在位图中的兴趣,并且您可以在 ref 计数降至 0 时进行 Dispose。

但这里最简单的方法是让 GC 完成工作。只要确保所有对包装器的引用字段都尽快为空。

【讨论】:

    【解决方案2】:

    所以,即使我不调用 Dispose(),CLR 最终也会在其他时候调用它。

    Finalizer 和 Dispose 方法是两个不同的东西。它“应该”这样做,但你不能肯定地说它会这样做。你不能假设这一点,你必须逐案检查。从你说的情况来看,在这种情况下听起来确实如此。

    如果我只是让位图保持原样,让终结器处理它们,那会不会太糟糕了?

    是的,因为指定终结器会导致垃圾收集器的额外工作。它会自动将其提升到CG的下一个generation,这意味着资源不会尽快释放。通常,当有人实现 Dispose 时,他们会抑制终结器调用,从而防止这种情况发生。

    只要一个对象实现了 IDisposable,就应该将它封装在 using 语句中。

    using(var bitmap = new BitMap())
    {
    ....
    
    }
    

    在这种情况下,由于 dispose 方法在终结器中,而且您确实需要,因此您不能使用 dispose 方法并让终结器来处理它。这不好,但你可以做到。根据您所说,这可能是不重构代码的最佳做法。

    【讨论】:

    • 关于第一点,是的,我可以,因为终结器正在调用 Dispose() 方法。
    • 关于第二点,也许资源不会尽快释放,但这比为了摆脱位图而进行复杂设计要好,我会说。
    • @devoured elysium:它不应该是一个复杂的设计。您可能已经知道何时删除图像(当您将其设置为 null 时),因此您只需在将其设置为 null 之前调用 .Dispose()
    • @Will:设置 (1) 对 null 的引用不是正确的条件。
    • 不,你是对的,这是在没有更多引用之后。通常对于图像,我习惯于只有一个引用(在显示对象内,通常是更新时显示的最后一个图像),所以我插入新的并处理旧的。
    【解决方案3】:

    位图的生命周期是多少?换句话说,是什么触发了位图的创建以及从那时到何时可以安全地销毁它们之间发生了哪些“事件”?

    如果在创建和生命周期结束之间没有发生用户/外部事件,那么最好将代码构造成创建位图的代码也会破坏它们。

    在 C# 中,最好将它们包装在 using 语句中:

    using (Bitmap bitmap = CreateBitmap())
    {
        DoWork(bitmap);
    }
    

    这保证了位图仅在需要时保留,不再保留。

    如果在创建和生命周期结束之间发生用户/外部事件(并且在处理这些事件时需要访问位图),情况并非如此简单,但这也意味着您保留了持久性引用到某个地方的位图,这会阻止它们被垃圾收集。

    【讨论】:

    • 正如我所说,我必须在很多地方使用它们。我不能只用标签来包装它们。
    • 但是所有这些“很多地方”是否都在创建位图的点之上的同一个调用堆栈中,或者它们是否分布在多个线程/多个 Web 请求/多个 UI 事件上?
    • 在这种情况下,除非您明确删除引用它们的任何字段/缓存,否则它们将永远不会被最终确定/垃圾收集,如果您正在这样做,那么您可以简单地调用 dispose 之前取消引用对象 - 这比依靠垃圾收集器调用终结器要高效得多。
    • 你有多个 UI 事件和线程; Bitmap 类的实例成员不能保证是线程安全的。您如何同步对常见实例的访问?
    【解决方案4】:

    根据我对您问题的理解,我建议您关注

    在您保留图像对象的类级别变量的类中扩展 IDisposable 接口,并在 Dispose() 方法中释放该图像对象。

    我也认为你不需要担心你传递给其他方法的位图对象,你不需要在那个特定的类中处理它。只需确保将其放置在创建位图实例的类中即可。

    希望这会对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-28
      相关资源
      最近更新 更多