HttpClient: ¿Se canceló una tarea?

Resuelto One Developer asked hace 9 años • 9 respuestas

Funciona bien cuando tenemos una o dos tareas, sin embargo, arroja el error "Se canceló una tarea" cuando tenemos más de una tarea en la lista.

ingrese la descripción de la imagen aquí

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();
}
One Developer avatar Mar 21 '15 13:03 One Developer
Aceptado

Hay 2 razones probables por las que TaskCanceledExceptionse lanzaría un:

  1. Algo llamó Cancel()al CancellationTokenSourcetoken de cancelación asociado antes de que se completara la tarea.
  2. Se agotó el tiempo de espera de la solicitud, es decir, no se completó dentro del lapso de tiempo especificado en HttpClient.Timeout.

Supongo que fue un tiempo muerto. (Si se tratara de una cancelación explícita, probablemente lo habría descubierto). Puede estar más seguro inspeccionando la excepción:

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.
}
Todd Menier avatar Mar 23 '2015 15:03 Todd Menier

Me encontré con este problema porque mi Main()método no esperaba a que se completara la tarea antes de regresar, por lo que se Task<HttpResponseMessage>canceló cuando se cerró el programa de mi consola.

C# ≥ 7,1

Puede hacer que el método principal sea asincrónico y esperar la tarea.

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

C#<7.1

La solución fue llamar myTask.GetAwaiter().GetResult()( Main()de esta respuesta ).

Ben Hutchison avatar Sep 10 '2017 02:09 Ben Hutchison
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);

Lo anterior es el mejor enfoque para esperar una solicitud grande. Estás confundido unos 30 minutos; Es un tiempo aleatorio y puedes dar el tiempo que quieras.

En otras palabras, la solicitud no esperará 30 minutos si obtienen resultados antes de los 30 minutos. 30 min significa que el tiempo de procesamiento de la solicitud es de 30 min. Cuando se produjo el error "La tarea fue cancelada" o se solicitó una gran cantidad de datos.

Navdeep Kapil avatar Oct 14 '2019 12:10 Navdeep Kapil

Otra posibilidad es que no se espere el resultado por parte del cliente. Esto puede suceder si algún método en la pila de llamadas no usa la palabra clave await para esperar a que se complete la llamada.

Manish avatar Sep 10 '2017 15:09 Manish

Promocionando el comentario de @JobaDiniz a una respuesta:

No haga lo obvio y deseche la HttpClientinstancia, aunque el código "parezca correcto":

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

¡ La eliminación de la instancia puede provocar que se cancelen HttpClientlas siguientes solicitudes HTTP iniciadas por otras instancias !HttpClient

Lo mismo ocurre con la nueva sintaxis RIAA de C#; un poco menos obvio:

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

En cambio, HttpClientla interfaz es un poco tosca y la forma correcta de usarla en dotnet core 2.1+ es:

Utilice una instancia estática o singleton HttpClientcon PooledConnectionLifetime...

Básicamente, almacene en caché una instancia estática de HttpClientsu aplicación o biblioteca y reutilícela:

static HttpClient client = new HttpClient();

async Task<HttpResponseMessage> Method() {
  return client.GetAsync(request);
}

(Todos los Async()métodos de solicitud son seguros para subprocesos ).


Sin embargo, Microsoft recomienda no utilizar directamente new HttpClient()! En su lugar, utilice .NET Framework tanto para dotnet core como para .NET Framework IHttpClientFactory. Una advertencia: los documentos IHttpClientFactorysuelen ser para aplicaciones ASP.NET, pero es posible usarlos con cualquier aplicación CLI/GUI ).

(Para .NET Framework, "puede agregar el paquete Microsoft.Extensions.Http NuGet a cualquier proyecto compatible con .NET Standard 2.0"; consulte esta respuesta ).

Carl Walsh avatar Dec 21 '2020 22:12 Carl Walsh