IAsyncDisposable是否有参考实现?

7

使用IDisposable时,我经常使用一个抽象的基类,因为实现它非常困难。主要是因为您可以使用接口来处理管理和非托管引用,每个引用的处理方式都不同。

public abstract class Disposable : IDisposable
{
    ~Disposable() => this.Dispose(false);

    public bool IsDisposed { get; private set; }

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

    protected virtual void DisposeManaged() {}
    protected virtual void DisposeUnmanaged() {}

    protected void ThrowIfDisposed()
    {
        if (this.IsDisposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    private void Dispose(bool disposing)
    {
        if (!this.IsDisposed)
        {
            if (disposing)
            {
                this.DisposeManaged();
            }

            this.DisposeUnmanaged();
            this.IsDisposed = true;
        }
    }
}

使用IAsyncDisposable,我找不到一个参考实现。此外,我认为它主要处理托管资源,因为它太新了,没有需要异步处理的非托管资源。如果是这样的话,那么实现是否简单?


1
不,它也涵盖了非托管资源。请参见IAsyncDisposable文档中的注释部分 - ckuri
有趣,没注意到。然而,在实践中,我不知道有任何实际的非托管资源利用这个特性。 - Muhammad Rehan Saeed
我也不知道。而且我也不确定你如何从终结器中实际调用它:a)使用阻塞调用即DisposeAsync().Wait(),b)调用DisposeAsync()并忘记它,或c)不要从终结器中调用它(并接受可能未释放的非托管资源)。这我想是你问题的核心。 - ckuri
2
只有在需要时才使用它。这种情况应该非常少见,释放资源几乎总是足够快的。我能想到的一个具体例子是UWP,它是一个无情的异步API。如果您想在清理时删除文件,则需要使用deleteasync - Hans Passant
2个回答

4
我们为该框架决定的模式,并将在框架设计准则,第三版中发布,是:
public async ValueTask DisposeAsync()
{
    await DisposeAsyncCore().ConfigureAwait(false);
    Dispose(false);
    GC.SuppressFinalize(this);
}

protected virtual ValueTask DisposeAsyncCore()
{
    // Code goes here that is the async equivalent to Dispose(true)
}

当然,这意味着所有的IAsyncDisposable类型也是IDisposable。此外,sealed类型可以跳过DisposeAsyncCore()

4
更新:.NET文档现在有一篇文章实现DisposeAsync方法,描述了非密封类需要同时支持同步和异步处理的推荐IAsyncDisposable实现。它建议添加一个单独的虚拟DisposeAsyncCore方法:
using System;
using System.Text.Json;
using System.Threading.Tasks;

public class ExampleAsyncDisposable : IAsyncDisposable, IDisposable
{
    // To detect redundant calls
    private bool _disposed = false;

    // Created in .ctor, omitted for brevity.
    private Utf8JsonWriter _jsonWriter;

    public async ValueTask DisposeAsync()
    {
        await DisposeAsyncCore();

        Dispose(false);
        GC.SuppressFinalize(this);
    }

    protected virtual async ValueTask DisposeAsyncCore()
    {
        // Cascade async dispose calls
        if (_jsonWriter != null)
        {
            await _jsonWriter.DisposeAsync();
            _jsonWriter = null;
        }
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
            _jsonWriter?.Dispose();
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.

        _disposed = true;
    }
}

这个示例假设只有间接拥有的非托管资源可以异步进行处理。直接拥有的托管资源总是在protected virtual void Dispose方法中同步处理。


旧答案:与常规Dispose不同,DisposeAsync方法不应该从终结器中调用。终结器已经在专用线程中运行,并且不会阻塞任何内容,因此没有必要这样做。因此,DisposeAsync始终可以处理非托管和托管资源,您无需实现单独的DisposeAsync(bool disposing)方法。

在新的.NET Core类型 Utf8JsonWriter 的源代码中可以找到IAsyncDisposable实现示例(它是一个密封类,因此不使用虚方法):

public async ValueTask DisposeAsync()
{
    if (_stream == null)
    {
        // The conditions are ordered with stream first as that would be the most common mode
        if (_output == null)
        {
            return;
        }
    }

    await FlushAsync().ConfigureAwait(false);
    ResetHelper();

    _stream = null;
    _arrayBufferWriter = null;
    _output = null;
}

基本上,它应该与常规可释放模式中的Dispose(true)执行相同的操作,但是如果可能的话,异步进行释放。如果不可能异步处理释放,但仍需要出于某种原因实现此接口,则可以退回到同步释放,并在完成后返回已完成的ValueTask,就像在System.IO.Stream中所做的那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接