3

我运行这段代码:

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);
}

我得到

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

我认为它是因为某些任务已经处于取消状态而导致的。为什么Task.WhenAll在取消子任务的情况下方法会中断正常流程并引发异常?这种行为有什么好处?

然后,我尝试以下方法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);
}

并且它不会抛出异常。

第二个问题是:为什么Task.WhenAny在同一种情况下不抛出异常?我希望这两种方法都应该以相同的方式处理取消的任务:要么抛出异常,要么不抛出异常。

4

2 回答 2

6

Task.WhenAny旨在完成其中一项任务时完成,其中完成包括失败。实际上,当其中一项任务可能失败时,我发现它最有用。例如:

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)
{
...
}

在这里,我只需要完成其中一项任务(因此,WhenAny)。在这种情况下,我也想取消其他任务。然后我调用WhenAll以等待其他任务尊重取消请求,如果发生异常也会传播异常。

换句话说,Task.WhenAny它旨在让您在其他任务仍在运行时执行某些操作,并且不抛出异常以让您对其他任务执行任何操作很有用。Task.WhenAll仅在所有任务完成(成功与否)时完成。它可以抛出异常,因为没有任何东西需要您处理,计算已完成。

于 2013-08-13T13:27:33.007 回答
3

Task.WhenAny返回 aTask<Task>Task.WhenAll返回 a Task。无论第一个完成/取消/故障任务的状态如何,外部任务都会成功完成。内部任务是第一个取消的任务。因此,必须等待内部和外部任务才能OperationCanceledException被抛出。

例如:

await await Task.WhenAny(cancelledTask3);
于 2017-07-21T23:22:56.223 回答