As you had learnt in the previous posts, Tasks are executed asynchronously and parallel. Now what happens when there is an exception? How and where is it thrown?
This blog post answers all the above questions
1. What happens when an exception occurs?
When a task causes an exception, it first terminates the Task .E is caught,saved as a part of an AggregateException ae and stored in object’s Exception property
.Now, we know, that I synchronous programming, the exception propagates upward to the caller from the callee.
But in synchronous programming, the scenario is different. Here if the exception is unhandled, it is re thrown at a later time.
2. What do you mean by ‘Later Time’?
The exception AE is rethrown when it encounters .result,.Wait or WaitAll Task functions.The exception if not handled or touched, is rethrown when the task object is garbage collected.
3. What is the proper way to handle exceptions ?
4. What is AggregateException ?
.NET throws all the exception wrapped in a single Exception called Aggregate Exception. Now as we know, that each task can create many subtasks, which again can lead to exceptions. So an aggregate Exception returned by a task, contains all the exception returned by the subtasks.
Consider this scenario, Task A creates Task B and Task C, and in doing so Task A is considered the parent of both Task B and Task C. These relationships come into play primarily in regard to lifetimes. A Task isn’t considered completed until all its children have completed, so if you used Wait on Task A, that instance of Wait wouldn’t return until both B and C had also completed. So what happens if both throws an exception. Then both the exceptions are wrapped in an aggregate Exception and rethrown.
5 .How do we access the individual exceptions ?
We access it through the Aggregrate Exceptions class. But it might happen that the child of the parent’s task, spawns more child tasks which throws exception. Hence we can see a tree forming,whose leaves are exceptions.
By default, AggregateExceptions retains this hierarchical structure, which can be helpful when debugging because the hierarchical structure of the contained aggregates will likely correspond to the structure of the code that threw those exceptions. However, this can also make aggregates more difficult to work with in some cases. To account for that, the Flatten method removes the layers of contained aggregates by creating a new AggregateException that contains the non-AggregateExceptions from the whole hierarchy.
6. What is observing Exceptions ?
Now, as we said unhandled Exceptions will be rethrown during garbage collection. To avoid that ,there are 4 methods you can “observe” the exceptions or in other words allow the exceptions to be rethrown so that you can handle it.
Calling .Wait, .Result or .WaitAll on tasks will allow the task to re throw the exceptions. [task.waitany does not throw any exceptions)
Access tasks’ Exception property after completion of task.
Subscribe to TaskScheduler.UnobservedTaskException
Accessing the exception property after completion of the task will not re throw the exception. If the exception property is not null, then you would know that an exception was generated. Even if you don’t handle it, the compiler won’t rethrow during garbage collection as you have observed the exception