2016-02-09 72 views
4

下面是一個示例PLINQ查詢我在一個Windows服務中定期運行:PLINQ查詢中的評估順序是什麼?

var resultList = new List<Task<SendMailResult>>(); 

try 
{ 
    resultList = emailsToSend 
     .AsParallel().WithDegreeOfParallelism(10) 
     .Select(async e => 
     { 
      bool bSuccess = false; 

      if (await MailHelper.SendMailAsync(e.sTo, e.sSubject, e.sHTML) == true) 
      { 
       bSuccess = true; 
      } 

      return new SendMailResult 
      { 
       succeeded = bSuccess, 
       resultid = e.id 
      }; 
     }).ToList(); 

    Task.WaitAll(resultList.ToArray()); 
} 
catch (AggregateException aggEx) 
{ 
    foreach (var ex in aggEx.InnerExceptions) 
     Console.Out.WriteLine(ex.Message); 
} 

我的問題是 - 如果一個例外是在內部匿名async Func<EmailToSend, Task<SendMailResult>>被拋出(在調用MailHelper.SendMailAsync()最有可能的),然後調用AggregateException處理程序 - 以下.ToList()已被調用在任何點?

換句話說,是否有可能某些任務成功完成,直到沒有任何任務,並且在被捕獲到AggregateException處理程序之後的下面的代碼片段中,我可能會得到一個帶有非0 Count的resultList?或者,異常意味着從不調用ToList(),並且如果引發異常,resultList將始終爲空?

我意識到我與鯊魚在使用這種多線程能力的情況下游泳時沒有充分理解其含義。因此,這個問題!謝謝。

回答

1

.ToList()將在任何時候被調用,因爲Selectlaziness和結果列表將包含所有任務。
執行將以.ToList()方法調用開始。 Select將僅提供IEnumerable,可枚舉列表。
如果某些任務失敗,出現異常,您將捕獲它們,畢竟您將獲得任務列表,其中某些任務的狀態爲Faulted

編輯:
拋出異常將被Task.WaitAll方法調用被觸發,所以名單將之前創建,將有確切的項目算作初始集合。

更新:
關於Task.WaitAll內部結構:
如果你想知道它是如何工作的,你可以看source code
首先它collects all incompleted tasks然後waits爲這些任務來完成。
然後它從所有任務collects all inner exceptions,然後提出它作爲單一AggregateException

+0

有趣!因此,在*執行上面的代碼之後,* *有保證*,那麼resultList將是一個任務列表,在Counts中等於emailsToSend IEnumerable? – oflahero

+1

是的。因爲異常將僅在'Task.WaitAll'調用中拋出。它將在列表創建後調用,因此列表將具有預期的長度。 – Kote

+0

因此,任何進一步的處理都應該檢查如下內容: 'resultList.Where(t => t.IsFaulted == false).Select(t => t.Result).Where(...',right? – oflahero