Let's face it; sometimes things just go wrong with our code. Even with the simplified parallel programming model provided by the TPL, we still need to be able to handle our exceptions.
Tasks use System.AggregateException
to consolidate multiple failures into a single exception object. In this recipe, we will take a look at the simplest way to handle System.AggregateException
in our tasks
: the try
/catch
blocks.
The try-catch statement consists of a try block followed by one of more catch blocks, which specify handlers for different exceptions. The try block contains the guarded code that may cause the exception.
For this recipe we need to turn off the Visual Studio 2012 Exception Assistant. The Exception Assistant appears whenever a runtime exception is thrown, and intercepts the exception before it gets to our handler.
Let's return to our WordCount solution so we can see how to handle an AggregateException
thrown by a parallel task.
WordCount6
as the Solution name.using
statements are at the top of your Program
class:using System; using System.Linq; using System.Net; using System.Threading.Tasks;
System.Net.WebClient
by creating and throwing a System.Net.WebException
. In the Main
method of your Program
class, create System.Task
that looks as the following Task
:Task<int> task1 = Task.Factory.StartNew(() => { const string headerText = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"; Console.WriteLine("Starting the task."); var client = new WebClient(); client.Headers.Add("user-agent", headerText); var words = client.DownloadString(@"http://www.gutenberg.org/files/2009/2009.txt"); var ex = new WebException("Unable to download book contents"); throw ex; return 0; });
Task
, let's put in our try
/catch
blocks as shown in the following code snippet. In the catch
block, we will want to specifically catch System.AggregateException
.try { } catch (AggregateException aggEx) { }
try
block. The body of the try
block should be as shown in the following code snippet. There are a couple of subtle but important concepts in here that will be explained later in the chapter.try { task1.Wait(); if (!task1.IsFaulted) { Console.WriteLine("Task complete. Origin of Species word count: {0}",task1.Result); } }
catch
block. It should look as shown in the following code snippet:catch (AggregateException aggEx) { foreach (var ex in aggEx.InnerExceptions) { Console.WriteLine("Caught exception: {0}", ex.Message); } }
catch
block, let's finish up by prompting the user to exit, and waiting on the user to hit Enter.Console.WriteLine("Press <Enter> to exit."); Console.ReadLine();
All of this stuff has been pretty self-explanatory so far, but handling exceptions in task involves a couple of subtleties that need to be pointed out.
The task itself is pretty straightforward. Other than throwing the System.Net.WebException
, there is nothing out of the ordinary here.
Let's take a closer look at the try/catch blocks. The first statement in the try
block System.Threading.Task.Wait()
to wait on task completion. However, there is another purpose here. Unhandled exceptions thrown inside a task
are swallowed by the runtime and wrapped up in System.AggregateException
. It is your job to handle this.
The TPL also has the concept of AggregateException
being observed. If AggregateException
is raised by your task, it will only be handled if it is currently being observed. This is very important to understand. If you never take an action that causes the exceptions to be observed, you are going to have a problem. When the Task
object is garbage collected, the Finalize
method of the task
will see that the task
had unobserved exceptions, and it will throwSystem.AggregateException
. You will not be able to catch an exception thrown by the finalizer thread and your process will be terminated.
So how to you observe an AggregateException
, you ask? The Systm.Threading.Task
class has a few methods and properties call triggers that cause System.AggregateException
to be observed. A few of these are as follows:
Using any of these trigger
methods indicates to the runtime that you are interested in observing any System.AggregateException
that occurs. If you do not use one of the trigger
methods on the Task
class, the TPL will not raise any AggregateException
, and an unhandled exception will occur.
Now, let's take a look at the catch
block. System.AggregateException
can wrap many individual exception objects. In our catch
block, we need to loop through AggregateException.InnerExceptions
to take a look at all of the individual exceptions that occurred in a task.
It is important to note that there is really no way to correlate an exception from the AggregateExcetion.InnerExceptions
collection back to the particular task
that threw an exception. All you really know is that some operation threw an Exception
.
System.AggregateException
overrides the GetBaseException
method of exception, and returns the innermost exception, which is the initial cause of the problem.
3.145.43.122