асинхронные методы, отмена и статус задачи - PullRequest
0 голосов
/ 06 июля 2018

С учетом сигнатуры метода, например

Taks<int> ComputeAsync(..., CancellationToken cancellationToken)

, можно ожидать, что возвращаемое задание завершится как

  • RanToCompletion (и набор Result) или
  • FaultedException набор) или
  • 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 материалу?

1 Ответ

0 голосов
/ 06 июля 2018

Из вашего примера - неясно, с какой целью вы отказались от использования токена. Так что мне придется говорить от моего использования этого. Мы используем службы Windows и должны знать, было ли использовано событие OnStop. У объекта TokenSource есть метод Cancel, который сигнализирует Задаче, что нам нужно остановить операции. Логическое значение Token указывает, что запрос отменен. Бросив OperationCancelled - вы можете поймать это значение и обработать его изящно. Это все во имя синхронизации управления задачей. Ваш сценарий будет иметь такую ​​же потребность, если вы делаете это. Дополнительное примечание от 16.07.2008:

Вы вызываете в своем вопросе подпись (с правильным написанием задачи)

Task<int> ComputeAsync(..., CancellationToken cancellationToken)

Но ваш пример - создает массив задач, который не использует эту подпись ни в одной из задач. Ваш пример слишком неполный, чтобы дать ответ. (Кажется, вы не понимаете подписи задачи). Когда вы вызываете задачу с токеном отмены, ваше действие задачи находится там, где эти elipses есть в вашем примере подписи - и вы не показываете, что вы передаете в задачу.

Вы используете Task.FromResult, который говорит - Верните этот результат.

Если вы используете Visual Studio F12 по методу FromResult, вы увидите исходный код с этим комментарием, который гласит

Summary:
    //     Creates a System.Threading.Tasks.Task`1 that's completed 
successfully with the
    //     specified result.

Это полное несоответствие вашему вопросу. Вот ответ стекопотока о том, для чего предназначен FromResult Какая польза от Task.FromResult в C #

...