HttpClient - 任务被取消?

Posted

技术标签:

【中文标题】HttpClient - 任务被取消?【英文标题】:HttpClient - A task was cancelled? 【发布时间】:2015-03-21 06:15:49 【问题描述】:

当有一个或两个任务时它可以正常工作,但是当我们列出多个任务时会抛出错误“任务已取消”。

List<Task> allTasks = new List<Task>();
allTasks.Add(....);
allTasks.Add(....);
Task.WaitAll(allTasks.ToArray(), configuration.CancellationToken);


private static Task<T> HttpClientSendAsync<T>(string url, object data, HttpMethod method, string contentType, CancellationToken token)

    HttpRequestMessage httpRequestMessage = new HttpRequestMessage(method, url);
    HttpClient httpClient = new HttpClient();
    httpClient.Timeout = new TimeSpan(Constants.TimeOut);

    if (data != null)
    
        byte[] byteArray = Encoding.ASCII.GetBytes(Helper.ToJSON(data));
        MemoryStream memoryStream = new MemoryStream(byteArray);
        httpRequestMessage.Content = new StringContent(new StreamReader(memoryStream).ReadToEnd(), Encoding.UTF8, contentType);
    

    return httpClient.SendAsync(httpRequestMessage).ContinueWith(task =>
    
        var response = task.Result;
        return response.Content.ReadAsStringAsync().ContinueWith(stringTask =>
        
            var json = stringTask.Result;
            return Helper.FromJSON<T>(json);
        );
    ).Unwrap();

【问题讨论】:

内部异常说明了什么? 为什么要把CancellationToken作为参数而不使用呢? 我的原因是错误地处理了HttpClient,例如async Task&lt;HttpResponseMessage&gt; Method() using(var client = new HttpClient()) return client.GetAsync(request); 对于那些使用HttpClient 像@JobaDiniz(带有using())的人,请停止!原因:aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong 这是微软对HttpClient的最新推荐:docs.microsoft.com/en-us/dotnet/architecture/microservices/… 【参考方案1】:

TaskCanceledException 被抛出的可能原因有 2 个:

    在任务完成之前与取消令牌关联的 CancellationTokenSource 上称为 Cancel() 的东西。 请求超时,即未在您在 HttpClient.Timeout 上指定的时间跨度内完成。

我猜是超时了。 (如果是显式取消,您可能已经知道了。)您可以通过检查异常来更加确定:

try

    var response = task.Result;

catch (TaskCanceledException ex)

    // Check ex.CancellationToken.IsCancellationRequested here.
    // If false, it's pretty safe to assume it was a timeout.

【讨论】:

那么什么是可能的解决方案?我有一个类似的问题。 ***.com/questions/36937328/… @Dimi - 这已经很老了,但我使用的解决方案是将 Timeout 属性设置为更大的值:httpClient.Timeout = TimeSpan.FromMinutes(30) @RQDQ,你这个男人,男人!不使用构造函数为我解决了这个问题。在我的具体情况下,我想要以毫秒为单位的超时。使用TimeSpan.FromMilliseconds(Configuration.HttpTimeout) 而不是new TimeSpan(Configuration.HttpTimeout) 是一种享受。谢谢! @RQDQ httpClient.Timeout = TimeSpan.FromMinutes(30) 不是一个好方法,因为它会阻塞该特定线程 30 分钟,并且也不会到达 HTTP 端点(这是您的主要任务)。此外,如果您的程序在 30 分钟之前完成,那么您最有可能遇到ThreadAbortException。更好的方法是找出 HTTP 端点没有被命中的原因,它可能需要 *** 或一些受限的网络访问。 @AmitUpadhyay 如果调用是awaited,那么没有线程被阻塞。不是 UI 线程,不是线程池线程 其他后台线程,没有。【参考方案2】:

我遇到了这个问题,因为我的Main() 方法在返回之前没有等待任务完成,所以当我的控制台程序退出时Task&lt;HttpResponseMessage&gt; myTask 被取消了。

C#≥7.1

您可以make the main method asynchronous 并等待任务。

public static async Task Main()
    Task<HttpResponseMessage> myTask = sendRequest(); // however you create the Task
    HttpResponseMessage response = await myTask;
    // process the response

C#

解决方案是在Main()(来自this answer)中调用myTask.GetAwaiter().GetResult()

【讨论】:

【参考方案3】:
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);

以上是等待大请求的最佳方法。 你困惑了大约 30 分钟;这是随机时间,你可以给任何你想要的时间。

换句话说,如果他们在 30 分钟之前得到结果,请求将不会等待 30 分钟。 30 分钟表示请求处理时间为 30 分钟。 当我们发生错误“任务已取消”或大数据请求需求时。

【讨论】:

【参考方案4】:

另一种可能性是客户端没有等待结果。如果调用堆栈上的任何一种方法不使用 await 关键字来等待调用完成,就会发生这种情况。

【讨论】:

【参考方案5】:

将@JobaDiniz 的评论推广到答案:

不要做显而易见的事情并处理 HttpClient 实例,即使代码“看起来正确”:

async Task<HttpResponseMessage> Method() 
  using (var client = new HttpClient())
    return client.GetAsync(request);

处置HttpClient 实例可能会导致HttpClient 的其他实例启动的以下HTTP 请求被取消!

C# 的新 RIAA 语法也是如此;稍微不那么明显:

async Task<HttpResponseMessage> Method() 
  using var client = new HttpClient();
  return client.GetAsync(request);


相反,正确的方法是为您的应用或库缓存HttpClient 的静态实例,然后重复使用它:

static HttpClient client = new HttpClient();

async Task<HttpResponseMessage> Method() 
  return client.GetAsync(request);

Async() 请求方法are all thread safe。)

【讨论】:

【参考方案6】:

在我的 .net core 3.1 应用程序中,我遇到了两个问题,内部原因是超时异常。 1,一个是我得到聚合异常,在它的内部异常是超时异常 2、其他情况是任务取消异常

我的解决办法是

catch (Exception ex)
            
                if (ex.InnerException is TimeoutException)
                
                    ex = ex.InnerException;
                
                else if (ex is TaskCanceledException)
                
                    if ((ex as TaskCanceledException).CancellationToken == null || (ex as TaskCanceledException).CancellationToken.IsCancellationRequested == false)
                    
                        ex = new TimeoutException("Timeout occurred");
                    
                                
                Logger.Fatal(string.Format("Exception at calling 0 :1", url, ex.Message), ex);
            

【讨论】:

应该是ex.InnerException is TaskCanceledException? IsCancellationRequested 也是真的吗?【参考方案7】:

在我的情况下,控制器方法不是异步的,控制器方法内部调用的方法是异步的。

所以我想一直使用 async/await 到顶层以避免此类问题很重要。

【讨论】:

【参考方案8】:

另一个原因可能是,如果您正在运行服务 (API) 并在服务中放置断点(并且您的代码卡在某个断点处(例如 Visual Studio 解决方案显示 Debugging 而不是跑步))。然后从客户端代码中访问 API。因此,如果服务代码在某个断点处暂停,您只需在 VS 中按 F5。

【讨论】:

【参考方案9】:

我使用的是简单调用而不是 async。一旦我添加了await 并创建了方法async,它就开始正常工作了。

public async Task<T> ExecuteScalarAsync<T>(string query, object parameter = null, CommandType commandType = CommandType.Text) where T : IConvertible
        
            using (IDbConnection db = new SqlConnection(_con))
            
                return await db.ExecuteScalarAsync<T>(query, parameter, null, null, commandType);
            
        

【讨论】:

以上是关于HttpClient - 任务被取消?的主要内容,如果未能解决你的问题,请参考以下文章

HttpClient - 任务被取消 - 如何获得确切的错误消息?

Web Api 的 PostAsync HttpClient 错误 - System.AggregateException“任务已取消。”

httpclient简介说明

httpClient 配置与测试

HTTPClient模块的HttpGet和HttpPost

HttpClient 如何在一个地方捕获每个响应