简体   繁体   中英

General approach to handle Task cancellation correctly

I'm doing a code review, and I'm concerned about this pattern, seen all across that code:

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

The part I'm concerned about is handling OperationCanceledException . Shouldn't this code be also handling AggregateException and checking if the only inner exception is OperationCanceledException ?

I know Task.Wait or Task.Result would throw an AggregateException like that, rather than OperationCanceledException . The author of the code assured me she only uses async/await inside out and never uses Wait/Result . Thus, she doesn't like the idea of additionally observing AggregateException for cancellation. However, my point is some standard Task -based BCL APIs could still be wrapping OperationCanceledException with AggregateException , eg because they still might be accessing Task.Result internally.

Does it make sense? Should we be worrying about handling both OperationCanceledException and AggregateException to observe cancellation correctly?

However, my point is some standard Task-based BCL APIs could still be wrapping OperationCanceledException with AggregateException, eg because they still might be accessing Task.Result internally.

No, they won't do that.

Should we be worrying about handling both OperationCanceledException and AggregateException to observe cancellation correctly?

I would say no. It is, of course, possible that an AggregateException can contain an OperationCanceledException , but it can also contain other particular exceptions just as easily.

As long as you follow async best practices (ie, no async-over-sync or sync-over-async), then you don't have to worry about this.

Well, it's definitely technically possible which is very easy to verify with this code:

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 stores the AggregateException inside the returned task so awaiting it still throws AggregateException . So if you want to be diligent you would need to catch AggregateException too.

However, it's very unlikely that any method in the BCL would do that and if it did you have bigger issues than exception handling since your doing async over sync. I would be more worried about your own code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM