I’m trying to understand async/await in .net8, especially exception handling and I’m running into some confusion, especially regarding the documentation as while it specifies some of the approach it doesn’t give enough examples for me to understand what’s happening.
I’m using the following trivial code just to watch something work:
private static async Task Main(string[] args) {
var sleeps = new int[] { 1, 2, 3, 0 };
var tasks = new List<Task<int>>();
foreach (var t in sleeps)
{
Console.WriteLine("Submitting task: {0}", t);
tasks.Add(sleep(t));
}
Console.WriteLine("All tasks submitted, waiting for all to complete");
try
{
Task.WaitAll(tasks.ToArray());
} catch (DivideByZeroException e)
{
Console.WriteLine("One of the tasks failed: {0}", e);
}
Console.WriteLine("All tasks completed, collecting results");
foreach (var t in tasks)
{
Console.WriteLine("Task returned: {0}", t.Result);
}
}
private static async Task<int> sleep(int s)
{
Console.WriteLine("Sleeping for: [ {0} ]", s);
if (s == 0)
{
// this is just to throw something to be caught
throw new DivideByZeroException("0 can't be delayed");
}
await Task.Delay(s * 1000);
Console.WriteLine("tSlept for: [ {0} ]", s);
return s;
}
I’m following documentation from here:
and here:
According the docs I should try to throw exceptions in my async functions BEFORE entering the async portion of the code. Which you can see in my example I am, I am throwing a ZeroDivision exception before awaiting the Task.Delay.
When I run this code in visual studio, I would expect to hit my DivideByZeroException catch block, but instead I get a SystemAggregateException, which I understand is how .net collects all of the async exceptions, however the documentation explicitly states: When code awaits a faulted task, the first exception in the AggregateException.InnerExceptions collection is rethrown
. However, I am not getting the DivideByZero exception thrown at all, instead I am getting 2 SystemAggregate exceptions throw sequentially each with their first innerException being the DivideByZero exception.
What is going on here.
I have also coded the breakfast example here: Asynchronous programming with async and await and added the toaster on fire there is even more confusing, in that I DO NOT get an exception throw anywhere when I explicitly throw one. I must inspect the Task.Exception
to see if it was thrown.
So can anyone explain what is happening here?
What is the approach I would use if I wanted to submit 5 async tasks, wait for all of them to complete and then iterate over them to inspect either their Result
or Exception
properties? I am not understanding when to try/catch, when to inspect Task.Exception and why I’m getting SystemAggregate exceptions in my example code.
2
Answers
The
AggregateException
is not thrown by your asynchronoussleep
method. It is thrown by the synchronousTask.WaitAll
method:The best solution to your problem is most likely to replace the
Task.WaitAll
with the asynchronousTask.WhenAll
:This will also eliminate a warning that you are probably seeing regarding the
async Task Main
method not containing anyawait
.In case you want to wait synchronously the tasks, and you like the behavior of
await
of propagating just one exception, you can use the.GetAwaiter().GetResult()
like this:This will block the current thread until all tasks complete, and will propagate the exception of one of the failed tasks, either positionally or chronologically, depending on the .NET runtime version. You can look at this GitHub issue for details about which task’s exception is propagated.
Your exception is getting thrown by the sync portion of your
sleep
function, which means that the point which exception catching/unwinding takes place is here:It has nothing to do with tasks at all, this is strictly a synchronous exception thrown in a synchronous function (
Main
, before the first async point). If you want to catch it, that’s where yourtry..catch
should be.