新 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
方法会永远等待吗?
您可以将 WaitAsync
和 Forget
功能组合在一个扩展方法中,如下所示:
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
在取消之前或之后因错误完成,应用程序将崩溃并弹出消息 “您的应用程序中发生未处理的异常” .用户可以选择继续 运行 应用程序,方法是单击“继续”按钮:
我正在尝试使用新的 .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
方法会永远等待吗?
您可以将 WaitAsync
和 Forget
功能组合在一个扩展方法中,如下所示:
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
在取消之前或之后因错误完成,应用程序将崩溃并弹出消息 “您的应用程序中发生未处理的异常” .用户可以选择继续 运行 应用程序,方法是单击“继续”按钮: