【发布时间】: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设置标志。
标签: c# .net timeout task-parallel-library dotnet-httpclient