区分用户取消超时
Posted
技术标签:
【中文标题】区分用户取消超时【英文标题】:Distinguish timeout from user cancellation 【发布时间】:2012-10-01 02:37:00 【问题描述】:HttpClient
具有内置的超时功能(尽管都是异步的,即超时可以被认为与 http 请求功能正交,因此可以由通用异步实用程序处理,但除此之外)并且当超时开始时,它'会抛出一个TaskCanceledException
(包裹在AggregateException
中)。
TCE 包含一个等于CancellationToken.None
的CancellationToken
。
现在,如果我向HttpClient
提供我自己的CancellationToken
并使用它在操作完成(或超时)之前取消操作,我会得到完全相同的TaskCanceledException
,再次使用CancellationToken.None
。
还有没有办法,只查看抛出的异常,来确定超时是否取消了请求,而不必让我自己的CancellationToken
可以访问检查异常?
附:这可能是一个错误,CancellationToken
以某种方式错误地固定为CancellationToken.None
?在 使用自定义 CancellationToken 取消 的情况下,我希望 TaskCanceledException.CancellationToken
等于该自定义令牌。
编辑
把问题说清楚一点,访问原CancellationTokenSource
,很容易区分超时和用户取消:
origCancellationTokenSource.IsCancellationRequested == true
从异常中获取 CancellationToken
会给出错误的答案:
((TaskCanceledException) e.InnerException).CancellationToken.IsCancellationRequested == false
这里一个小例子,由于大众的需求:
public void foo()
makeRequest().ContinueWith(task =>
try
var result = task.Result;
// do something with the result;
catch (Exception e)
TaskCanceledException innerException = e.InnerException as TaskCanceledException;
bool timedOut = innerException != null && innerException.CancellationToken.IsCancellationRequested == false;
// Unfortunately, the above .IsCancellationRequested
// is always false, no matter if the request was
// cancelled using CancellationTaskSource.Cancel()
// or if it timed out
);
public Task<HttpResponseMessage> makeRequest()
var cts = new CancellationTokenSource();
HttpClient client = new HttpClient() Timeout = TimeSpan.FromSeconds(10) ;
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "url");
passCancellationTokenToOtherPartOfTheCode(cts);
return client.SendAsync(httpRequestMessage, cts.Token);
【问题讨论】:
请发布一段显示此行为的最小代码。 为什么启动任务的代码没有处理异常?那段代码应该已经有 CancellationToken,它可以管理该场景,然后只抛出您希望更高级别的 try/catch 块接收的异常,而不需要 TokenSource @FranciscoNoriega 有时您想分离代码,例如使其可重用,并在异步执行的代码中。因此,您最终会在与定义执行代码的位置不同的位置捕获异常。我不想传递所有涉及的状态,特别是当异常似乎在需要时提供该状态 - 除非它不像我认为的那样工作。看一下大致展示了我如何使用它的精简示例。 @EugeneBeresovksy,你有没有设法解决这个问题?我处于类似情况,我有一个应用程序,其中大部分异常处理都在一个地方完成。 @ArthurNunes 你可以做的是创建自己的带有标志的MyTaskCanceledException
。然后,您在可以访问原始CancellationTokenSource
的范围内捕获原始TaskCanceledException
,并将其重新打包到您自己的异常中,使用origCancellationTokenSource.IsCancellationRequested
设置标志。
【参考方案1】:
公认的答案当然是这个应该在理论上是如何工作的,但不幸的是,在实践中IsCancellationRequested
并没有(可靠地)设置在附加到异常的令牌上:
Cancelling an HttpClient Request - Why is TaskCanceledException.CancellationToken.IsCancellationRequested false?
【讨论】:
您可能无法使用附加的令牌,但如果您有原始令牌(传递给SendAsync
),您可以使用它。还是我错过了什么?
我明白了,我错过了这个问题只需要依赖异常数据。【参考方案2】:
是的,它们都返回相同的异常(也可能是由于内部使用令牌超时),但可以通过这样做很容易地找出:
catch (OperationCanceledException ex)
if (token.IsCancellationRequested)
return -1;
return -2;
所以基本上如果你遇到异常但你的令牌没有被取消,那么这是一个常规的 http 超时
【讨论】:
我在我的问题中只查看抛出的异常,并添加了更多细节以更清楚地说明我在问什么。OperationCanceledException
如果在请求挂起时处理了HttpClient
,或者如果调用了HttpClient.CancelPendingRequests
,也会抛出。如果您控制对这些方法的调用,则可以区分这些情况。
这个答案缺乏细节......token
来自哪里?它是异常的属性吗?在你改进它之前投反对票
@ympostor 原始问题中有信息。他正在提供自己的令牌,这就是它的来源。以上是关于区分用户取消超时的主要内容,如果未能解决你的问题,请参考以下文章