Exception Handling
- Unhandled exceptions that are thrown by user code that is running inside a task are propagated back to the calling thread that is waiting for the task to finish or that access the Result property.
- Unhandled exceptions should terminate the process. The calling code can handle them by enclosing the call in a try/catch statement.
- Task Infrastructure wraps one or more exceptions in
AggregateException
. TheAggregateException
hasInnerException
property that can be enumerated to examine all the original exceptions that were thrown
Handling Exception
Exception From Single Task
var task = Task.Run(() => throw new Exception("An exception occured"));
try
{
task.Wait();
}
catch(AggregateException ex)
{
Console.WriteLine(ex.InnerExceptions[0].Message);
}
Using Exception Property
- You can also retrieve the
AggregateException
exception from the task's Exception property. - You can check
IsCompleted
in a while loop for task completion instead ofWait
method. - Use
TaskStatus.Faulted
to check if any exception occurred.
var task = Task.Run(() => throw new Exception("An exception occured"));
while(!task.IsCompleted) {}
if(task.Status == TaskStatus.Faulted)
{
Console.WriteLine(task.Exception.InnerExceptions[0].Message);
}
Exceptions from Multiple Task
Task[] taskArray = {
Task.Run(() => throw new Exception("An exception occured from task 1")),
Task.Run(() => throw new Exception("An exception occured from task 2")),
Task.Run(() => throw new Exception("An exception occured from task 3"))
};
try
{
Task.WaitAll(taskArray);
}
catch(AggregateException ex)
{
foreach(var e in ex.InnerExceptions) {
Console.WriteLine(e.Message);
}
}
Exceptions from Attached Nested Task
var task = Task.Factory.StartNew(() => {
var childTask1 = Task.Factory.StartNew(()=> {
var childTask2 = Task.Factory.StartNew(() => throw new Exception("Exception from child2 task"), TaskCreationOptions.AttachedToParent);
throw new Exception("Exception from child1 task");
}, TaskCreationOptions.AttachedToParent);
});
try
{
task.Wait();
}
catch(AggregateException ex)
{
Console.WriteLine(ex.InnerExceptions[0].Message);
//child1
Console.WriteLine(((AggregateException)ex.InnerExceptions[0]).InnerExceptions[0].Message);
}
Flattening Exceptions from Attached Nested Task
Use Flatten
method to remove all nested aggregate exceptions and return original exceptions.
try
{
task.Wait();
}
catch(AggregateException ex)
{
foreach(var e in ex.Flatten().InnerExceptions) {
Console.WriteLine(e.Message);
}
}
Exception From Detached Child Task
Detached child task run independently of parent task
To propagate the exception from child task access Wait
or Result
.
var task = Task.Factory.StartNew(() => {
var childTask1 = Task<int>.Factory.StartNew(()=> {
throw new Exception("Exception from child1 task");
});
//To propagate the exception access childTask1.Wait or childTask1.Result
int result = childTask1.Result;
});
try
{
task.Wait();
}
catch(AggregateException ex)
{
foreach(var e in ex.Flatten().InnerExceptions) {
Console.WriteLine(e.Message);
}
}
Using handle method to filter exceptions
- You can use the
AggregateException.Handle
method to filter out exceptions that you can treat as "handled" without using any further logic. - Any exceptions for which the delegate returns false are rethrown in a new AggregateException instance immediately after the AggregateException.Handle method returns.
private static void UsingAggregateExceptionHandle()
{
try
{
var files = GetAllFiles(@"C:\temp");
foreach(var file in files)
Console.WriteLine(file);
}
catch(AggregateException ae)
{
foreach(var ex in ae.InnerExceptions)
Console.WriteLine("Tasks Exception: {0} {1}", ex.GetType().Name, ex.Message);
}
try
{
var files = GetAllFiles(null);
foreach(var file in files)
Console.WriteLine(file);
}
catch(AggregateException ae)
{
foreach(var ex in ae.InnerExceptions)
Console.WriteLine("Tasks Exception: {0} {1}", ex.GetType().Name, ex.Message);
}
}
private static string[] GetAllFiles(string path)
{
var task = Task.Run(() => Directory.GetFiles(path, "*.txt"));
try
{
return task.Result;
}
catch(AggregateException ae)
{
ae.Handle(x => {
if(x is DirectoryNotFoundException)
{
Console.WriteLine("directory not found exception handled");
}
return x is DirectoryNotFoundException;
});
return Array.Empty<String>();
}
}
Summary
- One or more exceptions are wrapped in an
AggregateException
. TheAggregateException
hasInnerException
property that can be enumerated to examine all the original exceptions that were thrown - Handle the exception by enclosing the
Wait
orResult
intry/catch
block - You can also retrieve the exception from the task's
Exception
property. - Use
Flatten
method to remove all nested aggregate exceptions and return original exceptions.