区分用户取消超时

Posted

技术标签:

【中文标题】区分用户取消超时【英文标题】:Distinguish timeout from user cancellation 【发布时间】:2012-10-01 02:37:00 【问题描述】:

HttpClient 具有内置的超时功能(尽管都是异步的,即超时可以被认为与 http 请求功能正交,因此可以由通用异步实用程序处理,但除此之外)并且当超时开始时,它'会抛出一个TaskCanceledException(包裹在AggregateException中)。

TCE 包含一个等于CancellationToken.NoneCancellationToken

现在,如果我向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 原始问题中有信息。他正在提供自己的令牌,这就是它的来源。

以上是关于区分用户取消超时的主要内容,如果未能解决你的问题,请参考以下文章

使用取消令牌为用户定义的函数设置超时

AngularJS取消超时

订单超时取消解决方案

订单超时取消解决方案

订单超时自动取消golang

如何区分等待(长时间超时)退出通知或超时?