Reputation: 685
I'm trying to run some tasks in parallel which each return a value. Once they are all complete I want to utilize those returned results. The issue I am having is capturing the exception if one of tasks throws one.
Following what I've seen on some other SO questions I think this simplified code below should work. When I run it in visual studio it however my exception does not get handled.
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);
}
}
What I am expecting should get printed (order might be changed due to things running in parallel):
Processing 1
Processing 2
Processing 3
Processing 4
exception throw on: 3
1 - success = True
2 - success = True
4 - success = True
Am I completely misunderstanding how Task.WaitAll is meant to work?
Upvotes: 1
Views: 1156
Reputation: 252
No, you are not. However, you are observing the exception twice. Task.WaitAll will throw the exception, and your code will output the information on the exception. Then, you'll get the exception again when you fetch the result of that task from t.Result (tasks still contains the task that threw the exception).
You have have at least two choices to solve this issue:
Here's the update for #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);
}
}
}
}
Upvotes: 1