Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Task.WhenAll throws TaskCanceledException and Task.WhenAny doesnt in the same test case

I run this code:

var cancellation = new CancellationTokenSource();
var cancelledTask1 = .....;//starting new long-running task that accepts cancellation.Token
var cancelledTask2 = .....;//starting new long-running task that accepts cancellation.Token

//then I request cancellation
cancellation.Cancel();
//some task gets cancelled before code below executes
try
{
    //wait for completion (some task is already in cancelled state)
    await Task.WhenAll(cancelledTask1, cancelledTask2);
}
catch (OperationCanceledException e)
{
    Logger.Debug("await WhenAll", e);
}

And I get

await WhenAll System.Threading.Tasks.TaskCanceledException: A task was canceled.

I assume that it trows because some task is already in cancelled state. Why does Task.WhenAll method breaks normal flow and throws the exception in case of cancelled child tasks? What is benefit from this behavior?

Then, I try the method Task.WhenAny:

var cancellation = new CancellationTokenSource();
var cancelledTask3 = .....;//starting new long-running task that accepts cancellation.Token

//then I request cancellation
cancellation.Cancel();
//the task gets cancelled before code below executes
try
{
    //wait for completion (the task is already in cancelled state)
    await Task.WhenAny(cancelledTask3);
}
catch (OperationCanceledException e)
{
    Logger.Debug("await WhenAny", e);
}

and it doesn't throw exceptions.

The second question is: why Task.WhenAny does not throw exceptions in the same case? I expect that both methods should handle cancelled tasks the same way: either throw exceptions or not.

like image 993
neleus Avatar asked Sep 01 '26 02:09

neleus


1 Answers

Task.WhenAny is designed to complete whenever one of the tasks completes, where completion includes failure. I actually find it most useful when one of the tasks might fail. For instance:

try
{
   await Task.WhenAny(task1,task2);
   cancellationToken.Cancel(); //cancel all tasks
   await Task.WhenAll(task1,task2); //wait for both tasks to respect the cancellation
}
catch (Exception x)
{
...
}

Here I only need one of the tasks to complete (hence, the WhenAny). In which case I would like to cancel the other tasks as well. Then I call WhenAll to wait for the other task to respect the cancellation request, and also propagate an exception if it occurred.

In other words, Task.WhenAny is designed to let you do something while other tasks are still running, and it is useful for it not to throw exceptions in order to let you do whatever you want with the other tasks. Task.WhenAll only completes when all the tasks complete (either successfully or not). It can throw the exception because there is nothing left for you to handle, the computation is complete.

like image 134
Doron Yaacoby Avatar answered Sep 03 '26 18:09

Doron Yaacoby



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!