Отмена. Часть 3: Обнаружение Отмены. Окончание
Начало
TaskCanceledException
Существует ещё один тип исключения для отмены: TaskCanceledException. Он выбрасывается некоторыми API вместо OperationCanceledException.
Как правило, рекомендуется не использовать TaskCanceledException. Некоторые API просто вызывают OperationCanceledException, даже если они имеют дело с отменёнными задачами. А поскольку TaskCanceledException является производным от OperationCanceledException, код обработчика исключений отмены может просто перехватывать OperationCanceledException, игнорировать TaskCanceledException, и это будет работать везде.
Внимание: не перехватывайте TaskCanceledException. Вместо этого перехватывайте OperationCanceledException.
OperationCanceledException.CancellationToken
OperationCanceledException имеет свойство CancellationToken. Это токен, вызвавший отмену. Это в том случае, если он установлен. Не все API устанавливают это значение для выбрасываемых ими исключений.
Если вашему коду необходимо определить, отменяет ли операцию он сам или это делает какой-то другой код, у вас может возникнуть соблазн использовать это свойство. Но лучше игнорировать его. Когда используются связанные токены отмены (об этом далее в этой серии), вполне возможно, что токен в этом свойстве на самом деле не является основной причиной отмены:
async Task DoSomethingAsync()
{
// Плохой код, не делайте так
using CancellationTokenSource cts = new();
// Создаём что-то, что может отменять cts
try
{
await DoAsync(cts.Token);
}
catch (OperationCanceledException ex)
when (ex.CancellationToken == cts.Token)
{
// Обработка отмены «нашим» токеном
}
}
В коде выше есть проблема: в зависимости от реализации DoAsync возможно, что отмена cts приведёт к тому, что DoAsync выдаст исключение OperationCanceledException, но токен, на который ссылается это исключение, будет отличаться от токена cts.
Если вам действительно необходимо выполнить специальную обработку случая, когда происходит отмена через конкретный токен, попробуйте что-то вроде этого:
async Task DoSomethingAsync()
{
using CancellationTokenSource cts = new();
// Создаём что-то, что может отменять cts
try
{
await DoAsync(cts.Token);
}
catch (OperationCanceledException ex)
when (cts.IsCancellationRequested)
{
// Обработка отмены «нашим» токеном
}
}
Технически, здесь мы проверяем не «привёл ли мой токен к отмене операции», а скорее «произошла ли отмена и запрашивал ли мой токен отмену». Но в подавляющем большинстве случаев такой проверки достаточно.
Внимание: не используйте OperationCanceledException.CancellationToken. Он может работать не так, как вы ожидаете.
Источник: https://blog.stephencleary.com/2022/03/cancellation-3-detecting-cancellation.html