新 Task.WaitAsync(CancellationToken) API 和异常

New Task.WaitAsync(CancellationToken) API and exceptions

我正在尝试使用新的 .NET 6 Task.WaitAsync(CancellationToken) API。 我想要完成的是取消等待任务,同时仍然能够尝试取消任务本身(它调用异步库,我不能确定它会观察到我传入的 cancellationToken ,或者至少不及时)并避免吞下它可能抛出的任何可能的异常。

所以,例如,假设我想调用一个异步方法:

private async Task DoSomethingAsync(CancellationToken cancellationToken)
{
    //do something before the call

    await Library.DoSomethingAsync(cancellationToken); // Let's pretend
        // this is a call to a libary that accepts a cancellationToken,
        // but I cannot be 100% sure it will be observed
}

来自按钮单击事件处理程序:

private async void button1_Click(object sender, EventArgs e)
{
    var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
    var tsk = DoSomethingAsync(cts.Token);
    try
    {
        await tsk.WaitAsync(cts.Token);
    }
    catch (Exception ex) when (ex is OperationCanceledException)
    {
        tsk.Forget();
    }
}

现在我确定等待最多持续 5 秒,但是当 OperationCanceledException 被捕获时,任务可能仍然是 运行,我不想吞下任何异常它可以抛出。 那现在不想等待怎么办呢?

我想在 catch 块中使用像这样的 FireAndForget 扩展方法:

public static async void Forget(this Task task)
{
    try
    {
        await task.ConfigureAwait(false);
    }
    catch (Exception)
    {
        throw;
    }
}

这是一个可以接受的模式,还是我应该相信图书馆并希望它迟早会被取消? 如果它永远不会这样做,Forget 方法会永远等待吗?

您可以将 WaitAsyncForget 功能组合在一个扩展方法中,如下所示:

public async static Task WaitAsyncAndThenOnErrorCrash(this Task task,
    CancellationToken cancellationToken)
{
    Task waitTask = task.WaitAsync(cancellationToken);
    try { await waitTask; }
    catch when (waitTask.IsCanceled) { OnErrorCrash(task); throw; }

    static async void OnErrorCrash(Task task)
    {
        try { await task.ConfigureAwait(false); }
        catch when (task.IsCanceled) { } // Ignore overdue cancellation
    }
}

用法示例:

private async void button1_Click(object sender, EventArgs e)
{
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
    try
    {
        await DoSomethingAsync(cts.Token).WaitAsyncAndThenOnErrorCrash(cts.Token);
    }
    catch (OperationCanceledException) { } // Ignore
}

如果 DoSomethingAsync 在取消之前或之后因错误完成,应用程序将崩溃并弹出消息 “您的应用程序中发生未处理的异常” .用户可以选择继续 运行 应用程序,方法是单击“继续”按钮: