【问题标题】:Dispose works fine, but not on terminatingDispose 工作正常,但不能终止
【发布时间】:2014-10-08 17:26:08
【问题描述】:

我有一个实现IDisposable 的类,因为它使用来自 GDI+ 的图像资源(Bitmap 类)。我用它来包装所有花哨的LockBits/UnlockBits。它工作正常,无论是当我调用Dispose() 还是使用using 语句时。

但是,如果我让程序终止而不进行处理,我会得到一个System.AccessViolationException。直觉上,我认为 GC 会像我一样调用Dispose,并且对象会优雅地释放资源,但事实并非如此。为什么?


这是IDisposable 代码:

private bool _disposing = false;

~QuickBitmap() {
    Dispose(false);
}

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

private void Dispose(bool safeDispose) {
    if (_disposing)
        return;

    SaveBits(); // private wrapper to UnlockBits
    bytes = null; // byte[] of the image
    bmpData = null; // BitmapData object

    if (safeDispose && bm != null) {
        bm.Dispose(); // Bitmap object
        bm = null;
    }

    _disposing = true;
}

这是正常工作的时候:

using (var qbm = new QuickBitmap("myfile.jpg"))
{
    // use qbm.GetPixel/qbm.SetPixel at will
}

当它不起作用时:

public static void Main (string[] args) {
   // this is just an example, simply constructing the object and doing nothing will throw the exception
   var qbm = new QuickBitmap(args[0]);
   qbm.SetPixel(0, 0, Color.Black);
   qbm.Save();
}

完整的例外是(没有内部例外):

An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

即使在不同的机器上,复制也是 100%。我确实知道我们应该使用usingDispose(),不使用它是不好的,所有这些东西。我只想知道:为什么会这样?为什么内存被“保护”?我违反了哪种访问权限?

【问题讨论】:

  • 只是好奇……你为什么认为LockBits 是“噱头”?
  • @EdS。因为它要求我继续使用IntPtr。我使用这个类来避免在更高级别的类中使用IntPtr
  • 您没有遵循规定的 Dispose 模式。
  • 对我来说几乎不像是一个“噱头”,但还可以
  • 有一个模式可以正确地创建一个对象,它同时具有一个disposer和一个finalizer,这个代码真的不是它的一个例子。规则一是除非您是垃圾收集器方面的专家,否则不要编写终结器。成为 GC 专家后,您首先会知道是否需要实现终结器,其次会知道如何安全地执行此操作。

标签: c# gdi+


【解决方案1】:

出现问题是因为您在实现中包含了不必要的终结器。从终结器执行的代码通常无法安全地访问托管对象。尽管您没有包含该方法的代码,但对 SaveBits 的调用可能会导致使用托管对象违反此规则。

最好的解决方案是简单地从QuickBitmap 中删除终结器,因为QuickBitmap 类不直接拥有非托管资源。

【讨论】:

  • 没有私有Bitmap 直接拥有非托管资源吗?
  • 不,Bitmap 是托管类,而不是非托管资源。未管理的资源可能是由Marshal.AllocHGlobal 创建的IntPtr,除了显式调用Marshal.FreeHGlobal 之外,不会通过任何方式释放。
【解决方案2】:

IDisposable 的整个要点是优雅地清理非托管资源。非托管资源的字面定义 是 GC 无法自行清理的资源。毫不奇怪,如果您不这样做,它将无法自行清理。可以通过 GC 完全清理的对象不需要是一次性的,这不需要手动处理。如果对象可以在没有手动处理的情况下被清理,那么它就不需要首先实现IDisposable

【讨论】:

  • 这根本不能回答我的问题。我很清楚 IDisposable 和 GC 的含义,我正在寻找的是手动或从析构函数调用 Dipose 之间的区别。
  • @Mephy 您询问是否需要手动处置资源以确保其被处置。你做到了,就是这样。如果从不需要实际使用IDisposable 接口,则首先不需要它。如果您确实需要确保对非托管资源进行清理,则需要手动处置它。仅此而已。
猜你喜欢
  • 1970-01-01
  • 2022-06-29
  • 2022-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多