HttpClient - 任务被取消了吗?
HttpClient - A task was cancelled?
当有一个或两个任务时它工作正常,但是当我们列出了多个任务时会抛出错误"A task was cancelled"。
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.
}
我 运行 进入这个问题是因为我的 Main()
方法在返回之前没有等待任务完成,所以当我的控制台程序退出时 Task<HttpResponseMessage> 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# < 7.1
解决方案是在 Main()
中调用 myTask.GetAwaiter().GetResult()
(来自 this answer)。
另一种可能是客户端没有等待结果。如果调用堆栈上的任何一个方法不使用 await 关键字来等待调用完成,就会发生这种情况。
另一个原因可能是,如果您是 运行 服务 (API) 并在服务中放置断点(并且您的代码卡在某个断点处(例如 Visual Studio解决方案显示 Debugging 而不是 运行))。然后从客户端代码中点击 API。因此,如果服务代码在某个断点处暂停,您只需在 VS 中按 F5。
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);
以上是等待大型请求的最佳方法。
你困惑了大约30分钟;时间是随机的,你可以给任何你想要的时间。
换句话说,如果在 30 分钟之前得到结果,请求将不会等待 30 分钟。
30 分钟表示请求处理时间为 30 分钟。
当我们出现错误"Task was cancelled",或者大数据请求需求时。
在我的情况下,控制器方法不是异步的,控制器方法内部调用的方法是异步的。
所以我想使用 async/await 一直到顶层以避免此类问题很重要。
在我的 .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);
}
我使用的是一个简单的调用而不是 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);
}
}
提升@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。)
当有一个或两个任务时它工作正常,但是当我们列出了多个任务时会抛出错误"A task was cancelled"。
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.
}
我 运行 进入这个问题是因为我的 Main()
方法在返回之前没有等待任务完成,所以当我的控制台程序退出时 Task<HttpResponseMessage> 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# < 7.1
解决方案是在 Main()
中调用 myTask.GetAwaiter().GetResult()
(来自 this answer)。
另一种可能是客户端没有等待结果。如果调用堆栈上的任何一个方法不使用 await 关键字来等待调用完成,就会发生这种情况。
另一个原因可能是,如果您是 运行 服务 (API) 并在服务中放置断点(并且您的代码卡在某个断点处(例如 Visual Studio解决方案显示 Debugging 而不是 运行))。然后从客户端代码中点击 API。因此,如果服务代码在某个断点处暂停,您只需在 VS 中按 F5。
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);
以上是等待大型请求的最佳方法。 你困惑了大约30分钟;时间是随机的,你可以给任何你想要的时间。
换句话说,如果在 30 分钟之前得到结果,请求将不会等待 30 分钟。 30 分钟表示请求处理时间为 30 分钟。 当我们出现错误"Task was cancelled",或者大数据请求需求时。
在我的情况下,控制器方法不是异步的,控制器方法内部调用的方法是异步的。
所以我想使用 async/await 一直到顶层以避免此类问题很重要。
在我的 .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);
}
我使用的是一个简单的调用而不是 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);
}
}
提升@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。)