在什么情况下等待被取消的任务抛出TaskCanceledException?

时间:2017-03-21 14:01:15

标签: c# async-await

我习惯于在处理异步操作的取消时执行异步模式:

public async Task InvokeAsync(CancellationToken cancellationToken)
{
    using(cancellationToken.Register(handler.Stop))
    {
        try
        {
            await handler.HandleAsync();
        }
        catch(HandlerStoppedException ex)
        {
            cancellationToken.ThrowIfCancellationRequested();
            throw;
        }
    }
}

该方法调用一个异步组件,该组件公开某种取消机制。当请求令牌信号取消时,取消令牌设置回调以调用取消机制。

我可以在测试中调用此方法在超时内运行其功能。

async Task TestInvoke()
{
    using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10))
    {
        try
        {
            await InvokeAsync(timeout.Token);
        }
        catch (TaskCancelledException ex)
        {
            if (ex.CancellationToken == timeout.Token)
            {
                throw new TimeoutException(
                    "Operation failed to complete in the allowed time.", ex);
            }

            throw;
        }
    }
}

我的期望是在OperationCanceledException方法中抛出async会导致方法返回的Task转换为“已取消”状态。然后我预计任何等待此取消任务的尝试都应该抛出TaskCanceledException

在我当前的场景中(代码与上面的代码非常相似)我在等待任务时得到OperationCanceledException。如果我检查任务的状态,我可以看到它处于“已取消”状态并且没有与之相关的异常。

更奇怪的是,如果我在该任务上调用Wait(),则会抛出包含预期AggregateException的{​​{1}}。

在什么情况下等待取消的任务会抛出TaskCanceledException而不是更典型的OperationCanceledException

1 个答案:

答案 0 :(得分:5)

  

在什么情况下等待取消的任务会抛出OperationCanceledException而不是更典型的TaskCanceledException

这个问题太宽泛了。即使列举了今天发生的所有场景,明天也可能会改变。

相反,我会说:

  • TaskCanceledException不是更典型的"。它最初用于基于动态任务的并行性,与异步编程无关。
  • OperationCanceledExceptionTaskCanceledException的基类。在您的代码中,您永远不应该抓住TaskCanceledException(除非您 执行基于动态任务的并行性并且需要访问TaskCanceledException.Task)。

改为抓住OperationCanceledException