取消一个HttpClient的请求 - 为什么TaskCanceledException.CancellationToken.IsCancellationRequested假的?TaskCancele

2023-09-03 02:27:47 作者:一笑倾城

由于以下code:

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 catch块内,但事实并非如此。我误解的东西吗?

I would expect ex.CancellationToken.IsCancellationRequested to be true inside the catch block, but it is not. Am I misunderstanding something?

推荐答案

是这样的话,因为的HttpClient 内部(在 SendAsync )使用的是 TaskCompletionSource 来重新present的异步操作。它返回 TaskCompletionSource.Task ,这就是你等待上的任务

That's the case because HttpClient internally (in SendAsync) is using a TaskCompletionSource to represent the async operation. It returns TaskCompletionSource.Task and that's the task you await on.

,然后调用 base.SendAsync 并注册一个延续上返回的任务取消/完成/故障 TaskCompletionSource 的任务,相应地。

It then calls base.SendAsync and registers a continuation on the returned task that cancels/completes/faults the TaskCompletionSource's task accordingly.

在取消的情况下使用 TaskCompletionSource.TrySetCanceled 其中的关联取消的任务有一个新的的CancellationToken 默认(的CancellationToken))。

In the case of cancellation it uses TaskCompletionSource.TrySetCanceled which associates the canceled task with a new CancellationToken (default(CancellationToken)).

您可以看到,通过查看 TaskCanceledException 。在 ex.CancellationToken.IsCancellationRequested ex.CancellationToken.CanBeCanceled 也是,这意味着该的CancellationToken ,因为它不是用一个创建不能被取消 CancellationTokenSource

You can see that by looking at the TaskCanceledException. On top of ex.CancellationToken.IsCancellationRequested being false ex.CancellationToken.CanBeCanceled is also false, meaning that this CancellationToken can never be canceled as it wasn't created using a CancellationTokenSource.

IMO应该使用 TaskCompletionSource.TrySetCanceled(的CancellationToken) 代替。这样的 TaskCompletionSource 将与的CancellationToken 通过在由消费者,而不是简单地默认的CancellationToken 。我认为这是一个错误(虽然未成年人之一),我在连接这件事。

IMO it should be using TaskCompletionSource.TrySetCanceled(CancellationToken) instead. That way the TaskCompletionSource will be associated with the CancellationToken passed in by the consumer and not simply the default CancellationToken. I think it's a bug (though a minor one) and I submitted an issue on connect about it.