【问题标题】:Erasing IBuffers in a Destructor在析构函数中擦除 IBuffer
【发布时间】:2016-04-26 18:11:30
【问题描述】:

我有一个内部管理 IBuffer 的类。数据是敏感的,所以我想让类确保缓冲区在被销毁之前为 0,以避免将这些位留在内存中。我有一个 Erase() 方法,如下所示:

public static void Erase(this IBuffer value)
{
    using (var writer = new DataWriter(value.AsStream().AsOutputStream()))
    {
        for (int i = 0; i < value.Length; i++)
            writer.WriteByte(0);

        var storeTask = Task.Run(async () => await writer.StoreAsync());
        storeTask.Wait();
    }
}

首先,我认识到我在这里使用 Task.Run 来调用非 CPU 绑定的异步方法是可疑的,但我还没有找到同步的等效方法。欢迎使用替代品。

我遇到的问题是,在调试模式下,并且大部分时间在发布模式下,它都能完美运行。但是,偶尔在 Release 模式下,在我的对象上运行 Finalize() 时会遇到异常:

System.Runtime.WindowsRuntime.dll 中出现“System.Runtime.InteropServices.InvalidComObjectException”类型的未处理异常

附加信息:Excep_InvalidComObject_NoRCW_Wrapper。欲了解更多信息,请访问http://go.microsoft.com/fwlink/?LinkId=623485

该网址实际上只谈论 .Net Native 中的异常优化,而不是这种特定的异常类型。

我认为原因可能与在我的 Erase 方法有机会完成之前被销毁的 IBuffer 有关。

我怎样才能正确地实现我想要的行为?

【问题讨论】:

  • 使用IDisposable,而不是终结器。
  • 但这不是要求类的使用者显式调用 Dispose 吗?那不是我想要的行为。 IDisposable 的文档说“你应该重写 Object.Finalize 以在消费者忘记调用 Dispose 的情况下释放非托管资源。”其中 Object.Finalize 在 C# 中通过实现析构函数被覆盖。
  • 如果您的课程处理不应保留在内存中的秘密,您的调用者需要显式调用Dispose(通过using)。 不要依赖 GC 销毁内存.

标签: c# destructor uwp


【解决方案1】:

C# 终结器与 C++ 析构函数不同,尽管有时使用术语“析构函数”来描述它们。 C# 终结器根本无法保证运行。如果终结器确实运行,则它相对于其他终结器的运行顺序未定义,因此您的终结器不能依赖访问本身具有终结器的对象(例如 COM 对象包装器)。

所以是的,如果你依赖终结器并且你的终结器尝试使用一个本身可能有终结器的对象,你可能会发现当你的终结器运行时,它尝试使用的对象可能已经清理干净。

确实,如果您实现Dispose(),一种策略是也实现一个终结器(另一种策略是使用SafeHandle 子类来包装非托管资源)。但这只是一个支持,并且由于不能保证终结器运行,它不是 100% 可靠的。实现终结器的指导并不是因为这是一种 100% 可靠的清理方法,而是因为如果您正在处理忘记调用 Dispose() 的错误客户端代码,它是您将获得的最接近的方法。


所以,是的……在 C# 中,正确的策略是实现 IDisposable 并要求希望安全清理内存的客户端确保他们遵守规则并在完成对象后调用 Dispose()


顺便说一句,就你的Task.Run() 而言……

您应该异步使用异步方法。例如。不要实现Erase(),而是实现EraseAsync()并在内部使用await

但是,如果您真的坚持等待它们,则无需将调用包装在您使用Task.Run() 执行的匿名async 方法中。这是主要的矫枉过正。只需等待StoreAsync() 返回的任务对象即可。你可以这样做,例如通过调用它的GetResults() 方法(应该阻塞直到结果实际可用),或者您可以使用AsTask() extension method 直接转换为Task&lt;T&gt; 对象,当然然后等待该任务对象。

【讨论】:

  • 谢谢,这给了我一些很好的背景信息。我也有一个合适的EraseAsync(),主要是因为我有一些方法级联异步/等待会造成破坏。但回想起来,我什至不知道为什么我首先要在Task.Run() 中封装一个等待的电话!
猜你喜欢
  • 2023-03-11
  • 2019-10-08
  • 1970-01-01
  • 2014-12-30
  • 2012-08-11
  • 2014-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多