23

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

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

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

后者完全符合令人烦恼的异常的定义:它是在完全非异常情况下抛出的异常,因此我必须捕获它才能恢复正常的控制流。幸运的是,它很容易被抓住,对吧?只需添加catch (AggregateException)- 哦等等,这与出现致命错误时抛出的类型相同。

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

如何防止WaitAll为取消的任务引发异常?

4

2 回答 2

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);
} 

如果所有异常都是 type TaskCanceledException,则该Handle方法不会抛出任何异常;否则AggregateException将抛出一个只包含未处理异常的新异常。

于 2011-12-30T16:34:41.283 回答
1

根据João Angelo 的建议,这里有一个任务类扩展

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);
            }
        }

    }
}
于 2014-10-22T11:20:11.537 回答