С учетом сигнатуры метода, например
Taks<int> ComputeAsync(..., CancellationToken cancellationToken)
, можно ожидать, что возвращаемое задание завершится как
RanToCompletion
(и набор Result
) или
Faulted
(и Exception
набор) или
Canceled
, если cancellationToken
запрашивает отмену
Как реализовать этот метод при реализации метода async
?
Проведя тестирование, я обнаружил, что бросание OperationCanceledException
из асинхронного метода, по-видимому, завершает задачу в состоянии Canceled
(независимо от того, какой токен заключен в исключение, независимо от того, является ли токен IsCancellationRequested
) :
var tasks = new[]
{
Task.FromResult(42),
Task.FromException<int>(new Exception("Boom!")),
Task.FromCanceled<int>(new CancellationToken(true)),
Task.FromException<int>(new OperationCanceledException(new CancellationToken(true)))
};
async Task<int> Await(Task<int> task) => await task;
foreach (var t in tasks)
Console.WriteLine($"{t.Status} - {Await(t).Status}");
Выход:
RanToCompletion - RanToCompletion
Faulted - Faulted
Canceled - Canceled
Faulted - Canceled
Однако я не могу найти ни документации, ни другой информации о вышеупомянутом поведении. Можно ли полагаться на разные версии фреймворка?
Если ответ «да», то cancellationToken.ThrowIfCancellationRequested()
будет делать все правильно, но не перехватывая OperationCanceledException
s (вероятно, из await
ed задач) может поставить задачу Canceled
, хотя !cancellationToken.IsCancellationRequested
. Таким образом, чтобы иметь предсказуемые и правильные результаты, нужно ли нам оборачивать каждый отменяемый асинхронный метод в попытку / улов, чтобы убедиться, что OCE не выброшено, если оно не справа CancellationToken
, или есть более элегантный способ?
Если ответ отрицательный, то должны ли мы вернуться к TaskCompletionSource
материалу?