簡體   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