和for/foreach中發生異常的表現一樣,Parallel迴圈中的任何異常都會使整個迴圈終止,注意由於整個迴圈是分塊同時進行的,因此整個迴圈不會立即終止(如果有一個線程進行中長時間工作的話,而且是發生在CancellationToken的ThrowIfCancellationRequested方法之後)。
代碼:
try
{
Parallel.For(0, 5, (i) =>
{
throw new Exception("異常。迭代數字:" + i);
});
}
catch (AggregateException ae)
{
foreach (var exp in ae.InnerExceptions)
Console.WriteLine(exp.Message);
}
將會輸出0-4的子集(也有可能是0-4全部輸出,由於5個線程都快速運行完成)。
與Parallel.For和ForEach不一樣,Parallel.Invoke總是會把所有任務都執行完,然後把所有的異常封裝在AggregateException中。
來看這段代碼:
try
{
Parallel.Invoke(() => { throw new Exception("1"); },
() => { Thread.Sleep(1500); throw new Exception("2"); },
() => { Thread.Sleep(3000); throw new Exception("3"); });
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
Console.WriteLine(ex.Message);
}
結果會輸出:
3
2
1
Task.WaitAll和Parallel.Invoke類似,任何一個(或多個)Task的異常不會影響任何其他Task的執行。
try
{
var t1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(700);
throw new Exception("1");
});
var t2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
throw new Exception("2");
});
Task.WaitAll(t1, t2);
}
catch (AggregateException ae)
{
foreach (var exp in ae.InnerExceptions)
Console.WriteLine(exp.Message);
}
同樣會輸出:
2
1
兩個異常都會在AggregateException中的InnerExceptions屬性中。
對於Task執行的異常(而不是Task.WaitAll),可以參考這篇文章:
.NET(C#) TPL:Task中未覺察異常和TaskScheduler.UnobservedTaskException事件
對於取消處理的異常可以參考:
.NET(C#) TPL:Task, Parallel, PLINQ中取消操作後的OperationCanceledException異常