【问题标题】:What's the recommended way to deal with leaked IAsyncDisposable instances?处理泄漏的 IAsyncDisposable 实例的推荐方法是什么?
【发布时间】:2019-04-14 16:19:22
【问题描述】:

我一直在熟悉 C# 8 和 .NET Core 3.0 中添加的一些东西(计划添加),并且不确定实现 IAsyncDisposable 的正确方法(在撰写本文时,这链接实际上没有指导)。

特别是,我不清楚在没有显式处置实例的情况下该怎么做 - 也就是说,它没有被包裹在 async using(...) 中并且 .DisposeAsync() 没有被显式调用。

我的第一个想法是做与实现IDisposable 时相同的事情:

  • 我的DisposeAsync() 实现调用DisposeAsync(bool disposing)disposing: true
  • 实现一个调用DisposeAsync(disposing: false)的终结器(使用~MyType()
  • DisposeAsync(bool disposing) 实际上释放和/或处置所有内容,如果 disposing == true 则禁止最终确定。

我担心在终结器中没有什么可以等待DisposeAsync(bool) 的结果,而在终结器中显式等待似乎真的很危险。

当然,“只是泄漏”似乎也不太理想。

为了具体说明,这里是一个(简化的)示例类,确实有一个终结器:

internal sealed class TestAsyncReader: IAsyncDisposable
{
    private bool IsDisposed => Inner == null;
    private TextReader Inner;
    internal TestAsyncReader(TextReader inner)
    {
        Inner = inner;
    }

    // the question is, should this exist?
    ~TestAsyncReader()
    {
        DisposeAsync(disposing: false);
    }

    private ValueTask DisposeAsync(bool disposing)
    {
        // double dispose is legal, but try and do nothing anyway
        if (IsDisposed)
        {
            return default;
        }

        // should a finalizer even exist?
        if (disposing)
        {
            GC.SuppressFinalize(this);
        }

        // in real code some resources explicitly implement IAsyncDisposable,
        //   but for illustration purposes this code has an interface test
        if (Inner is IAsyncDisposable supportsAsync)
        {
            var ret = supportsAsync.DisposeAsync();
            Inner = null;
            return ret;
        }

        // dispose synchronously, which is uninteresting
        Inner.Dispose();
        Inner = null;
        return default;
    }

    public ValueTask DisposeAsync()
    => DisposeAsync(disposing: true);
}

那么,有没有关于正确处理泄露的IAsyncDisposable 实例的指导?

【问题讨论】:

  • Here's the commitIAsyncDisposable 添加到Threading.Timer。不知道这是否有帮助,但这是其实施的一个例子......
  • Dispose 做了两件事:释放托管资源和释放非托管资源。我可以想象需要异步释放托管资源(例如,需要正常关闭的数据库连接),但我很难想到需要异步释放的非托管资源(内存、文件指针等)。对于任何托管资源,要么它需要被显式释放(例如事件订阅),要么理想情况下它应该被释放但它的终结器可以处理它,如果它不是 - 无论哪种方式,你的终结者不应参与其中。
  • 顺便说一句,尽量避免编写自己的终结器。它使您的对象分配成本更高,并且很容易搞砸(例如,大多数人不知道您的可终结对象可以在调用本机方法时被终结,这会导致各种问题)。使用SafeHandle 及其派生类更容易、更安全。这回避了你的问题。
  • Dispose(bool disposing) 是一个可怕的模式。为了所有神圣的爱,我们不要对DisposeAsync做同样的事情。
  • @bboyle1234:我have always recommended 仅对包装非托管资源的类型使用终结器。这些类型应该只有一个同步的Dispose。如果您遵循该模式,则永远不需要将终结器与 DisposeAsync 集成。

标签: c# asynchronous dispose


【解决方案1】:

基于它是如何在 .NET Core 类中实现的示例(如 here)和来自 there 的一些建议,我想说当您需要实现 IAsyncDisposable 时,最好的做法是实现IAsyncDisposableIDisposable。在这种情况下,IAsyncDisposable 将仅在需要异步处理时负责显式场景,而 IDisposable 应该根据一次性模式实践照常实现,它将服务于所有后备场景,包括当事情最终确定时的场景.因此你不需要像DisposeAsync(bool disposing) 这样的东西——异步处理不能也不应该在终结器中发生。唯一的坏消息是您必须支持两种资源回收路径(同步和异步)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-12
    • 2015-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多