我尝试并行运行某些任务,每个任务都返回一个值。一旦完成,我想利用那些返回的结果。我遇到的问题是如果其中一个任务抛出一个异常就捕获异常。
根据我在其他一些SO问题上看到的内容,我认为下面的简化代码应该可行。当我在visual studio中运行它时,我的异常无法处理。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
class Program
{
static void Main()
{
int[] ids = { 1, 2, 3, 4 };
Test t = new Test();
t.Process(ids);
}
}
class Test
{
public void Process(int[] ids)
{
var tasks = new List<Task<Tuple<int, bool>>>(ids.Count());
foreach (int id in ids)
{
//The task will run at an indeterminate time so create a copy of id as it may have a new value assigned before that occurs
int i = id;
var task = Task.Factory.StartNew(() => DoWork(i));
tasks.Add(task);
}
try
{
Task.WaitAll(tasks.ToArray());
}
catch (AggregateException ex)
{
foreach (var innerEx in ex.InnerExceptions)
{
Console.WriteLine(innerEx);
}
}
foreach (var t in tasks)
{
Tuple<int, bool> result = t.Result;
Console.WriteLine(string.Format("{0} - success = {1}", result.Item1, result.Item2));
}
}
private Tuple<int, bool> DoWork(int i)
{
Console.WriteLine(string.Format("Processing {0}", i));
Thread.Sleep(i * 500);
if (i == 3)
{
//This exception does not get caught.
throw new Exception(string.Format("exception thrown on: {0}", i));
}
return new Tuple<int, bool>(i, true);
}
}
我期待的应该打印(由于并行运行,订单可能会改变):
处理1
处理2
处理3
处理4
例外:3
1 - 成功=真实
2 - 成功=真实
4 - 成功=真实
我是否完全误解了Task.WaitAll的工作方式?
答案 0 :(得分:1)
不,你不是。但是,您正在观察异常两次。 Task.WaitAll将抛出异常,您的代码将输出异常信息。然后,当您从t.Result获取该任务的结果时,您将再次获得异常(任务仍然包含引发异常的任务)。
您至少有两种选择来解决此问题:
这里是#2的更新:
public void Process(int[] ids)
{
var tasks = new List<Task<Tuple<int, bool>>>(ids.Count());
foreach (int id in ids)
{
//The task will run at an indeterminate time so create a copy of id as it may have a new value assigned before that occurs
int i = id;
var task = Task.Factory.StartNew(() => DoWork(i));
tasks.Add(task);
}
foreach (var t in tasks)
{
try
{
Tuple<int, bool> result = t.Result;
Console.WriteLine(string.Format("{0} - success = {1}", result.Item1, result.Item2));
}
catch (AggregateException ex)
{
foreach (var innerEx in ex.InnerExceptions)
{
Console.WriteLine(innerEx.Message);
}
}
}
}