Общий подход к правильной обработке отмены задачи


Я делаю обзор кода, и меня беспокоит этот шаблон, видимый во всем этом коде:

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.Wait или Task.Result бросят AggregateException так, а не OperationCanceledException. Автор кода заверил меня, что она использует только async/await наизнанку и никогда не использует Wait/Result. Таким образом, ей не нравится идея дополнительно наблюдение AggregateException за отменой. Однако моя точка зрения заключается в том, что некоторые стандартные API BCL на основе Task все еще могут быть обернуты OperationCanceledException с AggregateException, например, потому что они все еще могут обращаться к Task.Result внутри.

Есть ли в этом смысл? Должны ли мы беспокоиться о том, чтобы правильно обрабатывать как OperationCanceledException, так и AggregateException, Чтобы правильно наблюдать отмену?

2 2

2 ответа:

Ну, это определенно технически возможно, что очень легко проверить с помощью этого кода:

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 будет делать это, и если бы у вас были большие проблемы, чем обработка исключений, так как вы делаете асинхронную поверх синхронизации. Я бы больше беспокоился за тебя. собственный код.

Однако я хочу сказать, что некоторые стандартные API BCL, основанные на задачах, все еще могут обернуть OperationCanceledException с AggregateException, например, потому что они все еще могут обращаться к задаче.Результат внутренне.

Нет, они не сделают этого.

Должны ли мы беспокоиться об обработке как OperationCanceledException, так и AggregateException, чтобы правильно наблюдать отмену?

Я бы сказал, что нет. Конечно, возможно , что AggregateException может содержать OperationCanceledException, но он также может содержать other particular exceptions так же легко.

До тех пор, пока вы следуете рекомендациям по асинхронности (например, нет асинхронной синхронизации или синхронизации по асинхронности), вам не нужно беспокоиться об этом.