Reputation: 556
It works fine when have one or two tasks however throws an error "A task was cancelled" when we have more than one task listed.
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();
}
Upvotes: 280
Views: 370388
Reputation: 5103
I ran into this issue because my Main
method wasn't waiting for the task to complete before returning, so the Task<HttpResponseMessage>
was being cancelled when my console program exited.
You can make the main method asynchronous and await the task.
public static async Task Main() {
using HttpResponseMessage response = await sendRequest(); // however you create the Task
// process the response
}
The solution was to call Task.GetAwaiter().GetResult()
in Main()
(from this answer).
Upvotes: 30
Reputation: 6949
Promoting @JobaDiniz's comment to an answer:
Do not do the obvious thing and dispose the HttpClient
instance, even though the code "looks right":
async Task<HttpResponseMessage> Method() {
using (var client = new HttpClient())
return client.GetAsync(request);
}
Disposing the HttpClient
instance can cause following HTTP requests started by other instances of HttpClient
to be cancelled!
The same happens with C#'s new RIAA syntax; slightly less obvious:
async Task<HttpResponseMessage> Method() {
using var client = new HttpClient();
return client.GetAsync(request);
}
Instead, HttpClient
interface is a bit crufty, and the correct way to use it in dotnet core 2.1+ is:
Use a static or singleton
HttpClient
instance withPooledConnectionLifetime
...
Basically, cache a static instance of HttpClient
for your app or library, and reuse it:
static HttpClient client = new HttpClient();
async Task<HttpResponseMessage> Method() {
return client.GetAsync(request);
}
(The Async()
request methods are all thread safe.)
However, Microsoft recommends you don't use don't directly use new HttpClient()
! Instead for both dotnet core and .NET Framework, use IHttpClientFactory
. One caveat: the docs for IHttpClientFactory
are often for ASP.NET apps but it is possible to use it any CLI/GUI app.)
(For .NET Framework, "you can add the Microsoft.Extensions.Http NuGet package to any .NET Standard 2.0-compliant project" --see this answer.)
Upvotes: 14
Reputation: 4124
in my .net core 3.1 applications I am getting two problem where inner cause was timeout exception. 1, one is i am getting aggregate exception and in it's inner exception was timeout exception 2, other case was Task canceled exception
My solution is
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);
}
Upvotes: 6
Reputation: 724
I was using a simple call instead of async
. As soon I added await
and made method async
it started working fine.
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);
}
}
Upvotes: 1
Reputation: 141
In my situation, the controller method was not made as async and the method called inside the controller method was async.
So I guess its important to use async/await all the way to top level to avoid issues like these.
Upvotes: 3
Reputation: 381
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);
The above is the best approach for waiting on a large request. You are confused about 30 minutes; it's random time and you can give any time that you want.
In other words, request will not wait for 30 minutes if they get results before 30 minutes. 30 min means request processing time is 30 min. When we occurred error "Task was cancelled", or large data request requirements.
Upvotes: 27
Reputation: 1
Another reason can be that if you are running the service (API) and put a breakpoint in the service (and your code is stuck at some breakpoint (e.g Visual Studio solution is showing Debugging instead of Running)). and then hitting the API from the client code. So if the service code a paused on some breakpoint, you just hit F5 in VS.
Upvotes: 0
Reputation: 1776
Another possibility is that the result is not awaited on the client side. This can happen if any one method on the call stack does not use the await keyword to wait for the call to be completed.
Upvotes: 17
Reputation: 39289
There's 2 likely reasons that a TaskCanceledException
would be thrown:
Cancel()
on the CancellationTokenSource
associated with the cancellation token before the task completed.HttpClient.Timeout
.My guess is it was a timeout. (If it was an explicit cancellation, you probably would have figured that out.) You can be more certain by inspecting the exception:
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.
}
Upvotes: 390