2011-08-17 50 views
1

我目前正在创建一个演示项目,向团队的其他成员展示他们如何使用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个例外被包含?

我在这里错过了什么?

编辑:@丹布莱恩特把它挂在头上;当我改变的代码如下:

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

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

回答

1

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

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

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

+0

有趣。我们来测试一下;如果我将任务分配给列表中的每个元素,然后WaitAll,我应该得到100然后... – Tejs

+0

那么你是正确的! – Tejs

1

一旦发现异常,任务就停止为调度任务。它返回到目前为止的例外情况。现有的任务可以完成(如果可以的话),并且只有实际运行的任务返回例外。任何仍在等待运行的任务都无法开始。 (请记住,任务不一定立即开始)

这里是我的博客上讲述前几个月的详细信息: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/