如何在不抛出TaskCanceledException的情况下等待任务?

时间:2011-12-30 16:25:11

标签: exception-handling task-parallel-library cancellation

我有一个创建一些任务的方法,然后在返回之前用WaitAll等待它们。问题是,如果这些任务被取消,那么WaitAll会抛出一个包含大量AggregateExceptionTaskCanceledExceptionvexing exception

这意味着WaitAll将在两种不同的情况下抛出异常:

  • 表示真正错误的例外情况。这意味着我们不知道如何处理这种情况;它们需要作为未处理的异常进行传播,直到它们最终终止该过程。
  • 表示用户单击“取消”按钮的例外情况。这意味着任务被取消并清理,程序应该继续正常运行。

后者恰好适用于{{3}}的定义:它是在完全非特殊情况下抛出的异常,所以我必须抓住它才能恢复正常的控制流程。幸运的是,它很容易捕获,对吧?只需添加catch (AggregateException)和 - 哦等等,这是在发生致命错误时被抛出的相同类型。

我需要在返回之前等待任务完成运行(我需要知道他们不再使用他们的数据库连接,文件句柄或其他任何东西),所以我确实需要WaitAll或类似的东西。如果任何任务出现故障,我确实希望这些异常作为未处理的异常传播。我只是不想取消取消。

如何阻止WaitAll为已取消的任务抛出异常?

2 个答案:

答案 0 :(得分:28)

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

答案 1 :(得分:1)

基于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 stackoverflow suggestion https://stackoverflow.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);
            }
        }

    }
}