Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this exception get thrown?

I use a set of tasks at times, and in order to make sure they are all awaited I use this approach:

public async Task ReleaseAsync(params Task[] TaskArray)
{
  var tasks = new HashSet<Task>(TaskArray);
  while (tasks.Any()) tasks.Remove(await Task.WhenAny(tasks));
}

and then call it like this:

await ReleaseAsync(task1, task2, task3);
//or
await ReleaseAsync(tasks.ToArray());

However, recently I have been noticing some strange behavior and set to see if there was a problem with the ReleaseAsync method. I managed to narrow it down to this simple demo, it runs in linqpad if you include System.Threading.Tasks. It will also work slightly modified in a console app or in an asp.net mvc controller.

async void Main()
{
 Task[] TaskArray = new Task[]{run()};
 var tasks = new HashSet<Task>(TaskArray);
 while (tasks.Any<Task>()) tasks.Remove(await Task.WhenAny(tasks));
}

public async Task<int> run()
{
 return await Task.Run(() => {
  Console.WriteLine("started");
  throw new Exception("broke");
  Console.WriteLine("complete");
  return 5;
 });
}

What I don't understand is why the Exception never shows up anywhere. I would have figured that if the Tasks with the exception were awaited, it would throw. I was able to confirm this by replacing the while loop with a simple for each like this:

foreach( var task in TaskArray )
{
  await task;//this will throw the exception properly
}

My question is, why doesn't the shown example throw the exception properly (it never shows up anywhere).

like image 399
Travis J Avatar asked May 20 '14 18:05

Travis J


1 Answers

TL;DR: run() throws the exception, but you're awaiting WhenAny(), which doesn't throw an exception itself.


The MSDN documentation for WhenAny states:

The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.

Essentially what is happening is that the task returned by WhenAny simply swallows the faulted task. It only cares about the fact that the task is finished, not that it has successfully completed. When you await the task, it simply completes without error, because it is the internal task that has faulted, and not the one you're awaiting.

like image 100
Kendall Frey Avatar answered Sep 18 '22 10:09

Kendall Frey