2011-08-17 60 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/