2010-11-18 56 views
20

我遇到了異常處理和並行任務的問題。Task.WaitAll和Exceptions

下面顯示的代碼開始2個任務並等待它們完成。我的問題是,如果一個任務拋出一個異常,catch處理程序永遠不會到達。

 List<Task> tasks = new List<Task>(); 
     try 
     {     
      tasks.Add(Task.Factory.StartNew(TaskMethod1)); 
      tasks.Add(Task.Factory.StartNew(TaskMethod2)); 

      var arr = tasks.ToArray();     
      Task.WaitAll(arr); 
     } 
     catch (AggregateException e) 
     { 
      // do something 
     } 

但是,當我使用下面的代碼來等待具有超時的任務時,捕獲到異常。

while(!Task.WaitAll(arr,100)); 

我似乎失去了一些東西,爲WaitAll文檔描述了我的第一次嘗試是正確的。請幫助我理解爲什麼它不起作用。

+1

TaskMethod1和TaskMethod2做了什麼?你在執行什麼線程?如果你可以把它變成一個簡短但完整的例子(比如我的答案),那真的會有所幫助。 – 2010-11-18 17:26:27

回答

21

不能重現這一點 - 它工作正常,我:

using System; 
using System.Threading; 
using System.Threading.Tasks; 

class Test 
{ 
    static void Main() 
    { 
     Task t1 = Task.Factory.StartNew(() => Thread.Sleep(1000)); 
     Task t2 = Task.Factory.StartNew(() => { 
      Thread.Sleep(500); 
      throw new Exception("Oops"); 
     }); 

     try 
     { 
      Task.WaitAll(t1, t2); 
      Console.WriteLine("All done"); 
     } 
     catch (AggregateException) 
     { 
      Console.WriteLine("Something went wrong"); 
     } 
    } 
} 

,打印「出事了」,就像我期望。

您的某項任務可能未完成嗎? WaitAll確實等待所有任務完成,即使有些任務已經失敗。

+2

感謝您的快速回答喬恩!我的問題是,其他任務取決於失敗的任務,因此它會在失敗的任務中永遠等待。我的想法是,當任務失敗時立即捕獲異常,事實並非如此。感謝您指出了這一點。 – thumbmunkeys 2010-11-18 17:59:03

+5

@pivotnig - 看看'創建任務延續'在這裏明確表示依賴 - http://msdn.microsoft.com/en-us/library/dd537609.aspx – 2010-11-18 18:28:15

+0

@thumbmunkeys沒有意識到這張貼了這個。這正是我在這裏問的:https://stackoverflow.com/q/47820918/695964 – KFL 2017-12-14 22:15:13

9

以下是我解決了這個問題,因爲在我的答案/問題(上圖)的評論提到:

呼叫者捕獲由任務募集的屏障正在協調的任何異常,並通知其他任務與強制取消:

CancellationTokenSource cancelSignal = new CancellationTokenSource(); 
try 
{ 
    // do work 
    List<Task> workerTasks = new List<Task>(); 
    foreach (Worker w in someArray) 
    { 
     workerTasks.Add(w.DoAsyncWork(cancelSignal.Token); 
    } 
    while (!Task.WaitAll(workerTasks.ToArray(), 100, cancelSignal.Token)) ; 

} 
catch (Exception) 
{ 
    cancelSignal.Cancel(); 
    throw; 
} 
+1

這很好。從未意識到'CancellationTokenSource'的這個用例。 – trailmax 2014-01-09 01:53:13

0

我試圖創建一個集合中的每個項目,這竟然是這樣一個電話:

var parent = Task.Factory.StartNew(() => { 
    foreach (var acct in AccountList) 
    { 
     var currAcctNo = acct.Number; 
     Task.Factory.StartNew(() => 
     { 
     MyLocalList.AddRange(ProcessThisAccount(currAcctNo)); 
     }, TaskCreationOptions.AttachedToParent); 
     Thread.Sleep(50); 
    } 
    }); 

每次添加子任務後,我都必須添加Thread.Sleep,因爲如果我沒有,那麼進程會傾向於用下一次迭代覆蓋currAcctNo。我的列表中有3個或4個不同的帳號,當它處理每個帳號時,ProcessThisAccount呼叫將顯示所有呼叫的最後一個帳號。一旦我把Sleep放進去,這個過程很好。

+0

這可能是有趣的:http://stackoverflow.com/questions/5009181/parallel-foreach-vs-task-factory-startnew – thumbmunkeys 2013-01-29 06:49:54

+0

Parallel.ForEach更有效? – Kiquenet 2014-01-01 12:38:57

+0

這個答案與原始問題有什麼關係?這裏沒有例外 – knocte 2016-10-19 03:53:12

相關問題