我们开始在应用外大量使用CancellationToken,因此我们必须相应地更改异常处理:
class Program
{
static async Task Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource(100);
await DoJob(cts.Token);
Console.WriteLine("Successfully finished");
}
private static async Task DoJob(CancellationToken ct)
{
try
{
await Task.Delay(1000, ct);
}
catch (Exception e) when(!(e is OperationCanceledException))
{
Console.WriteLine("Do cleanup in case of error.");
}
}
}
此代码背后的想法是,如果有人使用catch(Exception e)(请不要为此怪我)而忘了排除CancellationToken,则会执行错误处理,例如,记录操作失败。但这不是真的,id不会失败,只是被取消了。取消处理应与失败处理不同。
在我看来,在每个常规渔获物上写东西实际上都是一个大样板
catch (Exception e) when(!(e is OperationCanceledException))
是否有一些更少样板的更强大的解决方案?
答案 0 :(得分:3)
您可以创建一个接受Func<Task>
并捕获异常的方法,例如:
class Program
{
static async Task Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource(100);
await GeneralDoJobAndCatchException(() => DoJob(cts.Token));
Console.WriteLine("Successfully finished");
}
private static async Task GeneralDoJobAndCatchException(Func<Task> func)
{
try
{
await func();
}
catch (OperationCanceledException) { }
catch (Exception e)
{
Console.WriteLine("Do error handling");
}
}
private static async Task DoJob(CancellationToken ct)
{
await Task.Delay(1000, ct);
}
}
答案 1 :(得分:1)
我们遇到了完全相同的问题。主要是有一个while
循环检查CancellationToken
,但您必须捕获此异常。
我们创建了以下扩展方法:
public static async Task<TaskStatus> HideCancellationException(this Task task)
{
try
{
await task;
return task.Status;
}
catch (OperationCanceledException)
{
return TaskStatus.Canceled;
}
}
具有此扩展方法可以更改此代码:
while (!cancellationToken.IsCancellationRequested)
{
// do stuff here...
try
{
await Task.Delay(..., cancellationToken);
}
catch (OperationCanceledException)
{
// expected
}
}
类似这样:
while (!cancellationToken.IsCancellationRequested)
{
// Do stuff here.
await Task.Delay(..., cancellationToken).HideCancellationException();
}
请记住,Task<T>
显然没有过载,因为取消的情况下返回值为default
。您无法区分default
作为正常任务结果和default
作为取消结果。在这种情况下,最好捕获异常。
答案 2 :(得分:0)
通过Task.WhenAny
间接等待,然后查询已完成任务的状态,可以完全摆脱try-catch块:
private static async Task DoJob(CancellationToken ct)
{
var completedTask = await Task.WhenAny(Task.Delay(1000, ct));
if (completedTask.IsFaulted)
{
Console.WriteLine("Error: " + completedTask.Exception.InnerException);
}
else if (completedTask.IsCanceled)
{
// Do nothing
}
else // Success
{
// Do nothing
}
}