jgauffin
jgauffin

Reputation: 101166

Task.WaitAll, how to find the tasks causing AggregateException

Let's say that I got the following code:

var tasks = BuildTaskList();
try
{
    Task.WaitAll(tasks.ToArray());
}
catch (AggregateException exception)
{

}

How do I know which task threw which of the exceptions in exception.InnerExceptions?

Upvotes: 7

Views: 7315

Answers (4)

Shahar Shokrani
Shahar Shokrani

Reputation: 8762

Option1 (credit to @theodor-zoulias comment):

You can set the Exception.Data property with the task name:

var task1 = Task.Factory.StartNew(() =>
{
    try
    {
        throw new Exception();
    }
    catch (Exception exception)
    {
        exception.Data["task"] = "task1";
        throw exception;
    }
});

var task2 = Task.Factory.StartNew(() =>
{
    try
    {
        throw new Exception();
    }
    catch (Exception exception)
    {
        exception.Data["task"] = "task2";
        throw exception;
    }
});

try
{
    Task.WaitAll(task1, task2);
}
catch (AggregateException ae)
{
    foreach (var exception in ae.InnerExceptions)
    {
        Console.WriteLine($"Exception was thrown by {exception.Data["task"]}");
    }
}

Option2:

If you don't mind loosing the name of the exception's assembly information, You can set the Exception.Source (doc) property of the thrown exception based on the task name and look at it while iterating:

var t1 = Task.Factory.StartNew(() => 
{
    throw new Exception() { Source = "t1" };
});
var t2 = Task.Factory.StartNew(() => 
{
    throw new Exception() { Source = "t2" };
});

try
{
    Task.WaitAll(t1, t2);
}
catch (AggregateException ae)
{
    foreach (var exception in ae.InnerExceptions)
    {
        Console.WriteLine($"Exception was thrown by {exception.Source}");
    }
}

Will output:

Exception was thrown by t1

Exception was thrown by t2

Upvotes: 0

Dennis
Dennis

Reputation: 37780

        var t1 = Task.Factory.StartNew(() => Console.WriteLine("Task 1"));
        var t2 = Task.Factory.StartNew(() => Console.WriteLine("Task 2"));
        var t3 = Task.Factory.StartNew(() => { throw new InvalidOperationException(); });
        var t4 = Task.Factory.StartNew(() => Console.WriteLine("Task 4"));

        Task.Factory.ContinueWhenAll(new[] { t1, t2, t3, t4 }, tasks =>
            {
                foreach (var t in tasks)
                {
                    if (t.Status == TaskStatus.Faulted)
                    {
                        // this will run for t3
                        Console.WriteLine("This task has been faulted.");
                    }
                }
            });

Upvotes: 0

Matthew Watson
Matthew Watson

Reputation: 109762

var throwers = tasks.Where(task => task.Exception != null);

Upvotes: 2

Matt Smith
Matt Smith

Reputation: 17444

You still have the list of Tasks, and each Task has an Exception property. Using that you can figure out which exceptions belong with which Task.

But, if you can, it'd be better to use Task.WhenAll or TaskFactory.ContinueWhenAll than do a blocking Wait.

Upvotes: 9

Related Questions