Handling exceptions in task - C#. NET

Another of the useful areas of standardization introduced by the TPL is in exception handling. It doesn’t matter how well we design, write, and test our code, we still have to handle exceptions. An exception that is thrown but dealt with is known as a handled exception. An exception that is thrown and not dealt with is an unhandled exception. Unhandled exceptions are something to avoid in general programming, but they are especially dangerous in when using the TPL, because by default, they will cause your program to exit unpredictably. The TPL provides a consistent model for handling exceptions that occur while a task is executing them, and the following sections describe the different aspects of this model and show you how to leave all of your exceptions unhandled and override the default policy for dealing with unhandled exceptions. provides the quick guide for handling task exceptions.

Handling Task Exceptions

Handling Task Exceptions

Handling Basic Exceptions
Any exception that is thrown (and not caught) by a Task is squirreled away by the .NET Framework until you call a trigger member, such as Task.Wait(),
Task.WaitAll(), Task.WaitAny(), or Task.Result, at which point the trigger
member will throw an instance of System.Aggregate Exception.

The AggregateException type is used to provider a wrapper around one or more exceptions, and this is useful because methods such as WaitAll() coordinate multiple Tasks and may need to present you with multiple exceptions. This feature is also useful for Task chaining. An AggregateException is always thrown by the trigger methods, even when there has been only one exception thrown.

An example is always the best illustration, so this creates three Tasks, two of which throw exceptions. The main thread starts the tasks and then calls the static WaitAll() method, catches the AggregateException and prints out details of the exceptions thrown by the individual Tasks.

Basic Exception Handling

To get the exceptions that have been aggregated, you call the InnerExceptions property of AggregateException, which returns a collections of exceptions that you can enumerate. In the listing, tasks task1 and task2 throw exceptions, which are bundled up into an instance of Aggregate Exception. This exception is thrown when we call the Task.WaitAll() trigger method. One shortcoming of this approach to handling exceptions is that there is no obvious way of correlating exceptions that have been thrown to the task that threw them. In the code listing, the Exception.Source property is used to indicate that task1 is the source of the ArgumentOutOfRangeException.

Using an Iterative Handler
Generally, you will need to differentiate between the exceptions that you were expecting and the ones that are unexpected and you need to propagate. The AggregateException class provides the Handle() method, which allows you to specify a function delegate that will be called for each exception. Your function or lambda expression should return true if the exception is one that you can handle and false otherwise.

The “Cancelling Tasks” section of this chapter explained that you should throw an instance of the OperationCanceledException to acknowledge a cancellation request. That type of exception is likely to be one you will have to process most frequently. Listing shows you how to use the Aggregate Exception. Handle() method to differentiate between an exception thrown for a cancellation.

Using an Iterative Exception Handler

If you compile and run the code in Listing, you will see one of the exceptions NullReferenceException — being reported as unhandled. This is, of course, because the exception handler only marks OperationCanceledExceptions as handled.

Reading the Task Properties
An alternative to catching the exceptions is to use the properties of the Task class, in particular, the IsCompleted, IsFaulted, IsCancelled, and Exception properties. You still have to catch Aggregate Exception when you call any of the trigger methods, but you can use the properties to determine if a task has completed, thrown an exception, or been cancelled, and if an exception was thrown, you can get the details of the exception. Listing shows you how to use the Task properties.

Exception Handling with Task Properties

The code in the listing creates two tasks: one throws an exception, and the other waits for a CancellationToken to be cancelled. Once the tasks are started, we cancel the token and call Task.WaitAll() to allow the tasks to complete. We ignore any exceptions by catching and discarding AggregateException and then print the values of the Task properties to the console, getting the following results:

Task 1 completed: True

Task 1 faulted: True

Task 1 cancelled: False

System.AggregateException: One or more errors occurred.
---> System.NullReferenceException:

...details of exception...

Task 2 completed: True

Task 2 faulted: False

Task 2 cancelled: True

Main method complete. Press enter to finish.

The IsCompleted property will return true if the Task has completed and false otherwise. The IsFaulted property returns true if the Task has thrown an exception and false if it has not or if the Task has been cancelled. The IsCanceled property returns true if the Task has been cancelled.

Using a Custom Escalation Policy
If you don’t catch AggregateException when you call a trigger method, the .NET Framework will escalate the exceptions. By default, this means that the unhandled exceptions will be thrown again when your Task is finalized and cause your program to be terminated. Because you don’t know when the finalizer will be called, you won’t be able to predict when this will happen.

But, if you are determined not to handle the exceptions using one of the techniques described in the previous sections, you can override the escalation policy and supply your own code to call when an exception is escalated. You do this by adding an event handler to the static
System.Threading.Tasks.TaskScheduler.UnobservedTaskException member.
Don’t worry about the other members of the TaskScheduler class. This shows how to implement an escalation policy that writes the exception type to the console.

Custom Escalation Policy

The .NET Framework calls the event handler each time an unhandled exception is escalated. Notice that the UnobservedTaskExceptionEventArgs.SetObserved() method is called to tell the .NET Framework that the exception has been handled and should not be escalated any further. If you omit the call to SetObserved(), the exception will be escalated using the default policy. You can get the exception by calling the UnobservedTaskExceptionEventArgs.Exception property, which will return instances of AggregateException.


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

C#. NET Topics