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

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

当前回答

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

其他回答

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

我使用了一个简单的调用,而不是异步。当我添加等待和使方法异步开始工作良好。

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

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

C# ≥ 7.1

您可以使主方法异步化并等待任务。

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

c# < 7.1

解决方案是在Main()中调用myTask.GetAwaiter(). getresult()(从这个答案)。

推广@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()请求方法都是线程安全的。

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

所以我认为使用async/await来避免这些问题是很重要的。