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