繁体   English   中英

DisposeAsync 应该抛出后台任务异常,还是留给客户端显式观察?

[英]Should DisposeAsync throw background task exceptions, or leave it to the client to observe explicitly?

我不认为这个问题与"Proper way to deal with exceptions in DisposeAsync"重复。

假设我的 class 实现了IAsynsDisposable ,因为它有一个长时间运行的后台任务,并且DisposeAsync终止了该任务。 一个熟悉的模式可能是Completion属性,例如ChannelReader<T>.Completion (尽管ChannelReader没有实现IAsynsDisposable )。

DisposeAsync之外传播Completion任务的异常是否被认为是一种好习惯?

这是一个完整的示例,可以复制/粘贴到dotnet new console项目中。 注意 DisposeAsync 中的await this.Completion DisposeAsync

try
{
    await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
    await Task.Delay(TimeSpan.FromSeconds(3));
}
catch (Exception ex)
{
    Console.WriteLine(ex);
    Console.ReadLine();
}

class BackgroundService: IAsyncDisposable
{
    public Task Completion { get; }

    private CancellationTokenSource _diposalCts = new();

    public BackgroundService(TimeSpan timeSpan)
    {
        this.Completion = Run(timeSpan);
    }

    public async ValueTask DisposeAsync()
    {
        _diposalCts.Cancel();
        try
        {
            await this.Completion;
        }
        finally
        {
            _diposalCts.Dispose();
        }
    }

    private async Task Run(TimeSpan timeSpan)
    {
        try
        {
            await Task.Delay(timeSpan, _diposalCts.Token);
            throw new InvalidOperationException("Boo!");
        }
        catch (OperationCanceledException)
        {
        }
    }
}

或者,我可以在客户端代码中显式观察service.Completion (并在DiposeAsync中忽略其异常以避免它们可能被抛出两次),如下所示:

try
{
    await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
    await Task.Delay(TimeSpan.FromSeconds(3));
    await service.Completion;
}
catch (Exception ex)
{
    Console.WriteLine(ex);
    Console.ReadLine();
}

class BackgroundService: IAsyncDisposable
{
    public Task Completion { get; }

    private CancellationTokenSource _diposalCts = new();

    public BackgroundService(TimeSpan timeSpan)
    {
        this.Completion = Run(timeSpan);
    }

    public async ValueTask DisposeAsync()
    {
        _diposalCts.Cancel();
        try
        {
            await this.Completion;
        }
        catch
        {
            // the client should observe this.Completion
        }
        finally
        {
            _diposalCts.Dispose();
        }
    }

    private async Task Run(TimeSpan timeSpan)
    {
        try
        {
            await Task.Delay(timeSpan, _diposalCts.Token);
            throw new InvalidOperationException("Boo!");
        }
        catch (OperationCanceledException)
        {
        }
    }
}

是否有关于哪个选项更好的共识?

现在,我已经确定了一个可重用的助手 class LongRunningAsyncDisposable这是一个要点),它允许:

  • 启动后台任务;
  • 通过随时以线程安全、并发友好的方式调用IAsyncDisposable.DisposeAsync来停止此任务(通过取消令牌);
  • 配置DisposeAsync是否应该重新抛出任务的异常(在进行清理之前, DisposeAsync将等待任务完成);
  • 可以通过LongRunningAsyncDisposable.Completion属性随时观察任务的状态、结果和异常;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM