如何在不抛出TaskCanceledExceptions的情况下等待任务?
问题描述:
我有一个方法可以创建一些任务,然后在返回之前用WaitAll等待它们。问题是,如果这些任务被取消,那么WaitAll会抛出包含大量TaskCanceledException的AggregateException。如何在不抛出TaskCanceledExceptions的情况下等待任务?
这意味着,为WaitAll将在两种不同的情况下抛出异常:
- 异常指示一个真正的错误。这意味着有一个我们不知道如何处理的情况;他们需要传播为未处理的异常,直到它们最终终止进程。
- 指示用户单击取消按钮的异常。这意味着任务被取消和清理,程序应该继续正常运行。
后者适合正视到vexing exception的定义:它是在一个完全非特殊情况下抛出一个异常,所以我要抓住它,以恢复正常的控制流。幸运的是,它很容易捕捉,对吧?只需加上catch (AggregateException)
和 - 哦,等等,这是在发生致命错误时引发的相同类型。
我确实需要等待任务在我返回之前完成运行(我需要知道他们不再使用他们的数据库连接,文件句柄或其他任何东西),所以我确实需要WaitAll或其他东西类似。如果任何任务出现故障,我确实希望这些异常作为未处理的异常传播。我只是不希望取消例外。
如何防止WaitAll
为取消的任务抛出异常?
答
该AggregateException
提供了一个Handle
方法,可用于这些情况。例如,如果你想忽略TaskCanceledException
你可以这样做:
var all = new AggregateException(
new NullReferenceException(),
new TaskCanceledException(),
new TaskCanceledException(),
new InvalidOperationException(),
new TaskCanceledException());
try
{
throw all;
}
catch (AggregateException errors)
{
errors.Handle(e => e is TaskCanceledException);
}
如果所有的例外是TaskCanceledException
类型时,Handle
方法不会抛出任何异常;否则将抛出仅包含未处理的异常的新的AggregateException
。
答
基于João Angelo's suggestion,在这里不用一个任务类扩展
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MySharedLibrary.Extensions
{
public static class TaskExtensions
{
// This code is based João Angelo's * suggestion https://*.com/a/8681687/378115
// Use this when a CancellationTokenSource is used
public static void SafeWait(this Task TargetTask, CancellationTokenSource TargetTaskCancellationTokenSource)
{
if (TargetTaskCancellationTokenSource.IsCancellationRequested == false)
{
TargetTaskCancellationTokenSource.Cancel();
}
SafeWait(TargetTask);
}
// Use this when no CancellationTokenSource is used
public static void SafeWait(this Task TargetTask)
{
try
{
if (TargetTask.IsCanceled == false)
{
TargetTask.Wait();
}
}
catch (AggregateException errors)
{
errors.Handle(e => e is TaskCanceledException);
}
}
}
}
使用需要的CancellationToken过载。 – 2011-12-30 16:29:34
@HansPassant,那个超载的文档实际上并没有说明它使用了什么标记。它是否会忽略与该标记关联的TaskCanceledExceptions,或者如果标记被取消,它是否会简单地返回?我需要它不会返回,直到所有任务都停止运行。 – 2011-12-30 16:31:05
您使用CancellationToken取消任务。并筛选您现在得到的特定OperationCancelException。 – 2011-12-30 16:33:13