You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为封装IDisposable对象的Task实现IDisposable/IAsyncDisposable可靠模式

优化返回IDisposable对象的Task字段的Dispose实现,以及IAsyncDisposable适配方案

核心问题分析

你的场景中,类持有一个返回IDisposable对象的Task字段(比如对接SharePoint、谷歌云盘、Dropbox时,获取API令牌的Task,令牌关联了需释放的会话、连接等资源),原Dispose实现未覆盖Task的所有状态(已完成、执行中、失败),需要确保资源被正确清理,同时支持异步释放逻辑。

同步Dispose优化实现

首先新增CancellationTokenSource管控正在执行的Task,在Dispose中处理Task的三种状态,确保资源不泄漏:

public class CloudStorageClient : IDisposable
{
    private readonly CancellationTokenSource _cts = new();
    private Task<IDisposable>? _tokenResourceTask;
    private bool _disposed;

    // 初始化时启动令牌获取Task,复用令牌避免重复认证
    public CloudStorageClient()
    {
        _tokenResourceTask = AcquireTokenWithResourceAsync(_cts.Token);
    }

    // 模拟对接云API获取令牌及关联可释放资源的逻辑
    private async Task<IDisposable> AcquireTokenWithResourceAsync(CancellationToken ct)
    {
        // 实际场景中:调用云API获取令牌,创建关联的可释放资源(如HTTP客户端、会话)
        var tokenResource = new TokenResource();
        await Task.Delay(1000, ct); // 模拟API调用延迟
        return tokenResource;
    }

    // 业务方法:复用已获取的令牌
    public async Task ExecuteApiCallAsync()
    {
        if (_disposed) throw new ObjectDisposedException(nameof(CloudStorageClient));
        var tokenResource = await _tokenResourceTask!;
        // 使用tokenResource中的令牌调用云API
    }

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

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

        if (disposing)
        {
            // 取消正在执行的令牌获取操作
            _cts.Cancel();

            if (_tokenResourceTask != null)
            {
                try
                {
                    // 等待Task完成(无论成功、取消还是失败)
                    _tokenResourceTask.Wait();

                    // 仅当Task成功完成时,释放返回的IDisposable资源
                    if (_tokenResourceTask.Status == TaskStatus.RanToCompletion)
                    {
                        _tokenResourceTask.Result.Dispose();
                    }
                }
                catch (AggregateException ex)
                {
                    // 过滤取消异常,其他异常可按需记录日志
                    ex.Handle(e => e is OperationCanceledException);
                }

                _tokenResourceTask = null;
            }

            _cts.Dispose();
        }

        _disposed = true;
    }

    // 模拟令牌关联的可释放资源类
    private class TokenResource : IDisposable
    {
        public void Dispose()
        {
            // 实际逻辑:关闭HTTP连接、清理云API会话等
        }
    }
}

关键处理逻辑

  • 执行中Task:通过_cts.Cancel()终止操作,等待Task完成后,若成功则释放资源
  • 已完成Task:直接释放返回的IDisposable对象
  • 已失败Task:无需释放资源(未成功获取到可释放对象),仅捕获异常避免崩溃

异步释放:实现IAsyncDisposable

如果资源支持异步释放(如.NET 6+的HttpClient异步释放、云API的异步会话关闭),需实现IAsyncDisposable接口,避免阻塞调用线程:

public class CloudStorageClient : IDisposable, IAsyncDisposable
{
    private readonly CancellationTokenSource _cts = new();
    private Task<IAsyncDisposable>? _tokenResourceTask;
    private bool _disposed;
    private bool _disposedAsync;

    public CloudStorageClient()
    {
        _tokenResourceTask = AcquireTokenWithAsyncResourceAsync(_cts.Token);
    }

    private async Task<IAsyncDisposable> AcquireTokenWithAsyncResourceAsync(CancellationToken ct)
    {
        var tokenResource = new AsyncTokenResource();
        await Task.Delay(1000, ct);
        return tokenResource;
    }

    public async Task ExecuteApiCallAsync()
    {
        if (_disposed || _disposedAsync) throw new ObjectDisposedException(nameof(CloudStorageClient));
        var tokenResource = await _tokenResourceTask!;
        // 使用令牌调用云API
    }

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

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

        if (disposing)
        {
            // 同步场景下调用异步释放并阻塞等待,确保资源清理
            _ = DisposeAsync().AsTask().Wait();
        }

        _disposed = true;
    }

    public async ValueTask DisposeAsync()
    {
        if (_disposedAsync) return;

        try
        {
            _cts.Cancel();

            if (_tokenResourceTask != null)
            {
                try
                {
                    await _tokenResourceTask;

                    if (_tokenResourceTask.Status == TaskStatus.RanToCompletion)
                    {
                        await _tokenResourceTask.Result.DisposeAsync();
                    }
                }
                catch (OperationCanceledException)
                {
                    // 忽略取消异常
                }
                catch (Exception ex)
                {
                    // 按需记录失败日志
                }

                _tokenResourceTask = null;
            }

            await _cts.DisposeAsync();
        }
        finally
        {
            _disposedAsync = true;
        }
    }

    // 支持异步释放的令牌资源类
    private class AsyncTokenResource : IAsyncDisposable
    {
        public async ValueTask DisposeAsync()
        {
            // 实际逻辑:异步关闭HTTP连接、异步终止云API会话等
            await Task.Delay(100);
        }
    }
}

异步释放要点

  • 优先使用DisposeAsync()进行异步清理,避免阻塞线程
  • 同步Dispose()内部调用异步释放方法,确保同步场景下资源也能被清理
  • 处理Task状态的逻辑与同步版一致,但使用异步等待和异步释放API

注意事项

  • 所有业务方法需检查_disposed/_disposedAsync状态,避免对象已释放后继续操作
  • Dispose操作不应抛出异常,异常仅需按需记录日志
  • 令牌复用逻辑确保_tokenResourceTask仅初始化一次,避免重复认证

内容的提问来源于stack exchange,提问作者Brent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 11:37:37