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. TheAggregateExceptionhasInnerExceptionproperty 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
AggregateExceptionexception from the task's Exception property. - You can check
IsCompletedin a while loop for task completion instead ofWaitmethod. - Use
TaskStatus.Faultedto 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.Handlemethod 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. TheAggregateExceptionhasInnerExceptionproperty that can be enumerated to examine all the original exceptions that were thrown - Handle the exception by enclosing the
WaitorResultintry/catchblock - You can also retrieve the exception from the task's
Exceptionproperty. - Use
Flattenmethod to remove all nested aggregate exceptions and return original exceptions.