给出以下代码:
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块中成为true
,但事实并非如此。我误解了什么吗?
答案 0 :(得分:41)
就是这种情况,因为HttpClient
内部(SendAsync
)正在使用TaskCompletionSource
来表示async
操作。它返回TaskCompletionSource.Task
,这是您await
的任务。
然后调用base.SendAsync
并在返回的任务上注册一个延续,相应地取消/完成/完成TaskCompletionSource
的任务。
如果取消,则会使用TaskCompletionSource.TrySetCanceled
将已取消的任务与新CancellationToken
(default(CancellationToken)
)相关联。
您可以通过查看TaskCanceledException
来查看。 ex.CancellationToken.IsCancellationRequested
false
ex.CancellationToken.CanBeCanceled
之上的false
也是CancellationToken
,这意味着永远不能取消此CancellationTokenSource
,因为它不是使用{{1}创建的}}
IMO应该使用TaskCompletionSource.TrySetCanceled(CancellationToken)
代替。这样,TaskCompletionSource
将与消费者传入的CancellationToken
相关联,而不仅仅是默认的CancellationToken
。我认为这是一个错误(虽然是一个小错误),我提交了一个issue on connect。
答案 1 :(得分:1)
@Bengie 这对我不起作用。我不得不稍作改动。 IsCancellationRequested始终返回true,所以我不能依靠它。
这对我有用:
using (CancellationTokenSource cancelAfterDelay = new CancellationTokenSource(TimeSpan.FromSeconds(timeout)))
{
DateTime startedTime = DateTime.Now;
try
{
response = await request.ExecuteAsync(cancelAfterDelay.Token);
}
catch (TaskCanceledException e)
{
DateTime cancelledTime = DateTime.Now;
if (startedTime.AddSeconds(timeout-1) <= cancelledTime)
{
throw new TimeoutException($"An HTTP request to {request.Url} timed out ({timeout} seconds)");
}
else
throw;
}
}
return response;
答案 2 :(得分:0)
我将超时设置为Infinite以将其禁用,然后我传入了自己的取消令牌。
using(CancellationTokenSource cancelAfterDelay = new CancellationTokenSource(timespan/timeout))
...
catch(OperationCanceledException e)
{
if(!cancelAfterDelay.Token.IsCancellationRequested)
throw new TimeoutException($"An HTTP request to {request.Uri} timed out ({(int)requestTimeout.TotalSeconds} seconds)");
else
throw;
}