【问题标题】:Aggregate Exception Not Containing All Task Exceptions?不包含所有任务异常的聚合异常?
【发布时间】:2011-08-17 23:36:47
【问题描述】:

我目前正在创建一个演示项目,向我的团队的其他成员展示他们如何使用 TPL 来编写更好的代码。但是,我对一个我认为应该以另一种方式运作的问题感到困惑。代码:

// Example 7 - Even more exceptions

try
{
    var numberList = Enumerable.Range(0, 1000).AsParallel().Select(x =>
        {
            if (x % 2 == 0)
                throw new ApplicationException("Shazam!");

            return x;
        }).ToList();
}
catch (AggregateException e)
{
    int exceptionsAggregated = 0;
    e.Flatten().Handle(ex =>
        {
            if (ex is ApplicationException)
            {
                if ((ex as ApplicationException).Message == "Shazam!")
                    exceptionsAggregated++;
            }

            return true;
        });

    Console.WriteLine("Exceptions: " + exceptionsAggregated);
}

除了发生的情况外,聚合异常将包含 500 个内部异常,因为在 PLINQ 中调用的每个其他线程都会引发异常。但是,我在聚合异常中只得到 4 个异常。

我的第一个想法是,当 TPL 达到可以抛出的异常数量的限制时,它可能会终止运行。但是,我似乎找不到任何支持该主张的在线文章或文档。所以我在这里有点难过;什么会导致只包含 4 个异常?

我只是在这里遗漏了一些基本的东西吗?

编辑:下面的@Dan Bryant 把它钉在了头上;当我将代码更改为以下内容时:

// 示例 7 - 更多异常

try
{
    var tasks = Enumerable.Range(0, 100).Select(x => Task.Factory.StartNew(() =>
        {
            if (x % 2 == 0)
                throw new ApplicationException("Shazam!");

            return x;
        })).ToArray();

    Task.WaitAll(tasks);
}
catch (AggregateException e)
{
    int exceptionsAggregated = 0;
    e.Flatten().Handle(ex =>
        {
            if (ex is ApplicationException)
            {
                if ((ex as ApplicationException).Message == "Shazam!")
                    exceptionsAggregated++;
            }

            return true;
        });

    Console.WriteLine("Exceptions: " + exceptionsAggregated);
}

我正确地得到了正确数量的异常。问题解决了!

【问题讨论】:

    标签: c# exception console-application task-parallel-library


    【解决方案1】:

    一种可能性是并行 Select 正在创建一个父任务,每次迭代都有一个子任务。默认的TaskScheduler只会同时执行一定数量的任务;如果这些子任务中的任何一个失败,则父任务将失败,这意味着尚未启动的子任务将不会执行。

    这符合 Select 不应有副作用的概念,因为 Select 中间的失败将阻止后续枚举调用停止执行。与 Parallel 版本的不同之处在于,您可能会发生一些异常(由于部分并行执行),而“串行”Select 在枚举时只能抛出一个异常。

    另一种可能性是它创建固定数量的任务,然后通过并发阻塞集合为它们分配工作。一旦每个任务失败,它就会停止执行其分配的工作负载。我认为后一种解释实际上更有可能,但我必须研究实现才能确定。

    【讨论】:

    • 有趣。让我们测试一下;如果我为列表的每个元素分配一个任务,然后 WaitAll,那么我应该得到 100...
    【解决方案2】:

    一旦发现异常,这些任务就不再是调度任务。它返回到目前为止它所拥有的异常。允许完成现有任务(如果可以的话),您只能获取实际运行的任务的异常。任何仍在等待运行的任务都不会开始。 (请记住,任务不一定立即开始)

    这是我几个月前在博客上发布的更多信息:http://colinmackay.co.uk/blog/2011/02/14/parallelisation-in-net-40-part-2-throwing-exceptions/

    而且由于您使用的是 PLINQ,因此您还应该注意,除非您调用了调用 WaitAll 的东西,否则异常不会冒泡到调用线程。更多信息: http://colinmackay.co.uk/blog/2011/05/16/tasks-that-throw-exceptions/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-28
      • 2018-07-07
      • 2012-09-27
      • 1970-01-01
      • 2015-05-03
      • 2011-09-07
      • 1970-01-01
      相关资源
      最近更新 更多