Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will awaiting multiple tasks observe more than the first exception?

Tags:

Today my colleagues and I discussed how to handle exceptions in C# 5.0 async methods correctly, and we wondered if awaiting multiple tasks at once also observes the exceptions that do not get unwrapped by the runtime.

Consider the following code snippet:

async Task ExceptionMethodAsync() {     await Task.Yield();     throw new Exception(); }  async Task CallingMethod() {     try     {         var a = ExceptionMethodAsync();         var b = ExceptionMethodAsync();          await Task.WhenAll(a, b);     }     catch(Exception ex)     {         // Catches the "first" exception thrown (whatever "first" means)      } } 

What happens to the second task now? Both will be in a faulted state, but is the second task's exception now observed or unobserved?

like image 985
Peit Avatar asked Sep 18 '14 12:09

Peit


People also ask

Can you await a task twice?

await hides all this complexity from you, and it allows you to await the same task in ten different places (very useful for e.g. asynchronous lazy initialization). can I be assured that the method pointed by task wont be executed twice even if the task is running or ran already ? @BilalFazlani Yes, you can.

How do you handle exceptions in tasks?

Exceptions are propagated when you use one of the static or instance Task. Wait methods, and you handle them by enclosing the call in a try / catch statement. If a task is the parent of attached child tasks, or if you are waiting on multiple tasks, multiple exceptions could be thrown.

Does await throw AggregateException?

When awaiting a faulted task (one that has an exception set), await will rethrow the stored exception. If the stored exception is an AggregateException it will rethrow the first and discard the rest.

Does task WhenAll run in parallel?

WhenAll() method in . NET Core. This will upload the first file, then the next file. There is no parallelism here, as the “async Task” does not automatically make something run in in parallel.


1 Answers

Task.WhenAll returns a task and like all tasks the Exception property holds an AggregateException that combines all exceptions.

When you await such a task only the first exception will actually be thrown.

... Whether because of child tasks that fault, or because of combinators like Task.WhenAlll, a single task may represent multiple operations, and more than one of those may fault. In such a case, and with the goal of not losing exception information (which can be important for post-mortem debugging), we want to be able to represent multiple exceptions, and thus for the wrapper type we chose AggregateException.

... Given that, and again having the choice of always throwing the first or always throwing an aggregate, for “await” we opt to always throw the first

from Task Exception Handling in .NET 4.5

It's up to you to choose if you want to handle just the first using await task; (true in most cases) or handle all using task.Exception (as in my example below), but in both cases a and b would not raise an UnobservedTaskException.

var task = Task.WhenAll(a, b); try {     await task; } catch {     Trace.WriteLine(string.Join(", ", task.Exception.Flatten().InnerExceptions.Select(e => e.Message))); } 
like image 146
i3arnon Avatar answered Oct 12 '22 10:10

i3arnon