HttpClient - 任务被取消了?

Kar*_*mar 170 c# task-parallel-library dotnet-httpclient

当有一个或两个任务时它可以正常工作但是当我们列出了多个任务时会抛出错误"任务被取消".

在此输入图像描述

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();
}
Run Code Online (Sandbox Code Playgroud)

Tod*_*ier 245

有两种可能的原因TaskCanceledException会抛出:

  1. 一些所谓的Cancel()CancellationTokenSource与任务完成之前取消令牌相关联.
  2. 请求超时,即未在您指定的时间范围内完成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.
}
Run Code Online (Sandbox Code Playgroud)

  • @Dimi - 这是相当古老的,但我使用的解决方案是将Timeout属性设置为更大的值:`httpClient.Timeout = TimeSpan.FromMinutes(30)` (45认同)
  • @AmitUpadhyay如果呼叫已被等待,则没有线程被阻塞。不是UI线程,不是线程池线程,其他后台线程也没有。 (6认同)
  • @RQDQ`httpClient.Timeout = TimeSpan.FromMinutes(30)`不是一个好方法,因为它会阻塞该特定线程30分钟,也不会命中HTTP端点(这是你的主要任务).此外,如果您的程序在30分钟之前完成,那么您最有可能遇到`ThreadAbortException`.更好的方法是找出为什么HTTP端点没有被命中,它可能需要VPN或一些受限制的网络访问. (4认同)
  • 那么什么是可能的解决方案?我有一个类似的问题.http://stackoverflow.com/questions/36937328/get-asp-net-mvc5-webapi-token-fails-sometimes?noredirect=1#comment61434357_36937328 (3认同)
  • @RQDQ,你这个男人,伙计!不使用构造函数为我解决了问题.在我的具体情况下,我想要一个毫秒的超时.使用`TimeSpan.FromMilliseconds(Configuration.HttpTimeout)`而不是`new TimeSpan(Configuration.HttpTimeout)`起了作用.谢谢! (2认同)
  • @AmitUpadhyay 调用`HttpClient` 是异步的,所以无论你设置超时多长时间都不会阻塞线程,只要你正确使用它(即`await`ing 响应)。 (2认同)

Ben*_*son 17

我遇到了这个问题,因为我的Main()方法不是在返回之前等待任务完成,所以Task<HttpResponseMessage> myTask在我的控制台程序退出时被取消了.

解决的办法是打电话myTask.GetAwaiter().GetResult()Main()(从这个答案).


小智 17

var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);
Run Code Online (Sandbox Code Playgroud)

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

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


Car*_*lsh 12

将@JobaDiniz 的评论提升为答案:

HttpClient即使代码“看起来正确”,也不要做明显的事情并处置实例:

async Task<HttpResponseMessage> Method() {
  using (var client = new HttpClient())
    return client.GetAsync(request);
}
Run Code Online (Sandbox Code Playgroud)

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

C# 的新 RIAA 语法也会发生同样的情况;稍微不太明显:

async Task<HttpResponseMessage> Method() {
  using var client = new HttpClient();
  return client.GetAsync(request);
}
Run Code Online (Sandbox Code Playgroud)

相反,HttpClient接口有点粗糙,在 dotnet core 2.1+ 中使用它的正确方法是:

使用静态或单HttpClient例实例PooledConnectionLifetime...

基本上,为您的应用程序或库缓存一个静态实例HttpClient,然后重用它:

static HttpClient client = new HttpClient();

async Task<HttpResponseMessage> Method() {
  return client.GetAsync(request);
}
Run Code Online (Sandbox Code Playgroud)

Async()请求方法都是线程安全的。)


但是,微软建议 您不要使用,不要直接使用 new HttpClient()对于 dotnet core 和 .NET Framework,请使用IHttpClientFactory. 需要注意的是:文档IHttpClientFactory通常适用于 ASP.NET 应用程序,但可以在任何 CLI/GUI 应用程序中使用它。)

(对于 .NET Framework,“您可以将Microsoft.Extensions.Http NuGet 包添加到任何符合 .NET Standard 2.0 的项目中”——请参阅此答案。)


Man*_*ish 9

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


Kam*_*hid 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);
            }
Run Code Online (Sandbox Code Playgroud)