HttpClient - 任务被取消了?

时间:2015-03-21 06:15:50

标签: c# task-parallel-library dotnet-httpclient

当有一个或两个任务然后抛出错误时它工作正常"任务被取消"当我们列出多个任务时。

enter image description here

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();
}

9 个答案:

答案 0 :(得分:207)

有两个可能的原因导致TaskCanceledException被抛出:

  1. 在任务完成之前与取消令牌相关联的Cancel()上有CancellationTokenSource的内容。
  2. 请求超时,即在HttpClient.Timeout指定的时间范围内未完成。
  3. 我的猜测是暂停。 (如果是明确的取消,你可能会想到这一点。)你可以通过检查异常来更加确定:

    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.
    }
    

答案 1 :(得分:15)

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

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

答案 2 :(得分:9)

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

答案 3 :(得分:2)

var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);

以上是等待大型请求的最佳方法。 您大约30分钟感到困惑;这是随机的时间,您可以给任何时间。

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

答案 4 :(得分:2)

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

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

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

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。)

答案 5 :(得分:1)

在我的.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);
            }

答案 6 :(得分:0)

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

答案 7 :(得分:-1)

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

因此,我认为一路使用async / await至顶层以避免此类问题很重要。

答案 8 :(得分:-1)

我使用的是一个简单的电话,而不是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);
            }
        }