正确处理任务取消的一般方法

时间:2015-01-22 05:27:41

标签: c# .net task-parallel-library async-await cancellation

我正在进行代码审核,我对这种模式很关注,在整个代码中都看到了:

try
{
    await DoSomethingAsync();
    await DoSomethingElseAsync();
    // and so on...
}
catch (OperationCanceledException)
{
    // all good, user cancelled
    // log and return
    return;
}
// handle other particular exceptions
// ...
catch (Exception ex)
{
    // fatal error, alert the user
    FatalErrorMessage(ex);
}

我关注的部分是处理OperationCanceledException。此代码不应该同时处理AggregateException并检查唯一内部例外是否为OperationCanceledException吗?

我知道Task.WaitTask.Result会抛出AggregateException,而不是OperationCanceledException。该代码的作者向我保证,她只在内部使用async/await,从不使用Wait/Result。因此,她不喜欢另外观察AggregateException取消的想法。但是,我的观点是一些标准的基于Task的BCL API仍然可以OperationCanceledException包裹AggregateException,例如因为他们可能仍在内部访问Task.Result

有意义吗?我们是否应该担心处理OperationCanceledExceptionAggregateException以正确观察取消?

2 个答案:

答案 0 :(得分:4)

  

但是,我的观点是一些标准的基于任务的BCL API仍可能使用AggregateException包装OperationCanceledException,例如因为他们仍然可能在内部访问Task.Result。

不,他们不会这样做。

  

我们是否应该担心处理OperationCanceledException和AggregateException以正确观察取消?

我会说不。当然,可能 AggregateException可以包含OperationCanceledException,但它也可以轻松地包含other particular exceptions

只要您遵循异步最佳做法(即没有异步同步或同步异步),您就不必担心这一点。

答案 1 :(得分:3)

嗯,这绝对是技术上可行的,使用此代码很容易验证:

static void Main()
{
    Test().Wait();
}

static async Task Test()
{
    try
    {
        await ThrowAggregate();
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
    }
}

static async Task ThrowAggregate()
{
    ThrowException().Wait();
}

static async Task ThrowException()
{
    throw new OperationCanceledException();
}

ThrowAggregate将AggregateException存储在返回的任务中,等待它仍然会抛出AggregateException。所以,如果你想要勤奋,你也需要抓住AggregateException

但是,非常不可能 BCL中的任何方法都可以做到这一点,如果它确实存在问题,那么因为异步过度同步,你会遇到更大的问题。 我会更担心你自己的代码。