Отмена запроса HttpClient-почему TaskCanceledException.CancellationToken.IsCancellationRequested ложь?
учитывая следующий код:
var cts = new CancellationTokenSource();
try
{
// get a "hot" task
var task = new HttpClient().GetAsync("http://www.google.com", cts.Token);
// request cancellation
cts.Cancel();
await task;
// pass:
Assert.Fail("expected TaskCanceledException to be thrown");
}
catch (TaskCanceledException ex)
{
// pass:
Assert.IsTrue(cts.Token.IsCancellationRequested,
"expected cancellation requested on original token");
// fail:
Assert.IsTrue(ex.CancellationToken.IsCancellationRequested,
"expected cancellation requested on token attached to exception");
}
Я бы ожидал ex.CancellationToken.IsCancellationRequested на true внутри блока catch, но это не так. Я что-то не так понял?
1 ответов
это так, потому что HttpClient внутренне (в SendAsync) использует TaskCompletionSource представлять async операции. Он возвращается TaskCompletionSource.Task и это задача, которую вы await on.
затем он называет base.SendAsync и регистрирует продолжение возвращенной задачи, которая отменяет/завершает / неисправности TaskCompletionSource задача соответственно.
в случае отмены он использует TaskCompletionSource.TrySetCanceled который связывает отмененную задачу с новой CancellationToken (default(CancellationToken)).
вы можете видеть это, глядя на TaskCanceledException. Поверх ex.CancellationToken.IsCancellationRequested будучи false ex.CancellationToken.CanBeCanceled тоже false, т. е. CancellationToken никогда не может быть отменен, поскольку он не был создан с помощью CancellationTokenSource.
IMO он должен использовать TaskCompletionSource.TrySetCanceled(CancellationToken). Таким образом,TaskCompletionSource будет связан с CancellationToken переданный потребителю, а не просто по умолчанию CancellationToken. Я думаю, что это ошибка (хотя и незначительная), и я подал Ан вопрос о подключении об этом.