Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the Task.WhenAny not throw an expected TimeoutException?

Please, observe the following trivial code:

class Program
{
    static void Main()
    {
        var sw = new Stopwatch();
        sw.Start();
        try
        {
            Task.WhenAny(RunAsync()).GetAwaiter().GetResult();
        }
        catch (TimeoutException)
        {
            Console.WriteLine("Timed out");
        }
        Console.WriteLine("Elapsed: " + sw.Elapsed);
        Console.WriteLine("Press Enter to exit");
        Console.ReadLine();
    }

    private static async Task RunAsync()
    {
        await Observable.StartAsync(async ct =>
        {
            for (int i = 0; i < 10; ++i)
            {
                await Task.Delay(500, ct);
                Console.WriteLine("Inside " + i);
            }
            return Unit.Default;
        }).Timeout(TimeSpan.FromMilliseconds(1000));
    }
}

Running it outputs:

Inside 0
Inside 1
Elapsed: 00:00:01.1723818
Press Enter to exit

Note, no Timed out message.

Now, if I replace Task.WhenAny with Task.WhenAll here is what I get:

Inside 0
Inside 1
Timed out
Elapsed: 00:00:01.1362188
Press Enter to exit

Note the presence of the Timed out message this time.

And, if I remove the Task.WhenAll wrapper at all and call RunAsync directly:

Inside 0
Inside 1
Timed out
Elapsed: 00:00:01.1267617
Press Enter to exit

The Timed out message is there, as expected.

So what is the deal with Task.WhenAny ? It obviously interrupts the asynchronous method, but where is the TimeoutException?

like image 698
mark Avatar asked Jul 21 '15 16:07

mark


People also ask

Does task Whenany start the tasks?

As others have noted, Task. WhenAll only aggregates the tasks; it does not start them for you.

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.


1 Answers

Task.WhenAny doesn't rethrow exceptions from the individual tasks (unlike Task.WhenAll):

"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."

From Task.WhenAny

That means that it will complete successfully no matter what without any type of exceptions.

To actually rethrow the exception of the individual completed task you need to await the returned task itself:

var completedTask = await Task.WhenAny(tasks); // no exception
await completedTask; // possible exception

Or in your case:

Task.WhenAny(RunAsync()).GetAwaiter().GetResult().GetAwaiter().GetResult();
like image 141
i3arnon Avatar answered Oct 19 '22 10:10

i3arnon