HttpClient: ¿se canceló una tarea?

191

Funciona bien cuando tiene una o dos tareas, sin embargo, arroja un 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();
}
Karthikeyan Vijayakumar
fuente
¿Qué dice la excepción interna?
RagtimeWilly
1
¿Por qué tomas un CancellationTokenparámetro y no lo usas?
Jim Aho
1
La razón para mí fue deshacerse HttpClientpor error, por ejemploasync Task<HttpResponseMessage> Method(){ using(var client = new HttpClient()) return client.GetAsync(request); }
JobaDiniz
3
Para aquellos que usan HttpClientcomo @JobaDiniz (con un using()), ¡deténgase! La razón: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Philippe

Respuestas:

274

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

  1. Algo llamado Cancel()en el CancellationTokenSourceasociado con el token de cancelación antes de completar la tarea.
  2. Se agotó el tiempo de espera de la solicitud, es decir, no se completó dentro del intervalo de tiempo especificado HttpClient.Timeout.

Supongo que fue un tiempo de espera. (Si se tratara de una cancelación explícita, probablemente lo habría resuelto). Puede estar más seguro al inspeccionar 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
fuente
3
Entonces, ¿cuál es una posible solución? Tengo un problema similar. stackoverflow.com/questions/36937328/…
Desarrollador
49
@Dimi: esto es bastante antiguo, pero la solución que utilicé fue establecer la propiedad Timeout en un valor mayor:httpClient.Timeout = TimeSpan.FromMinutes(30)
RQDQ
2
@RQDQ, eres el hombre, hombre! No usar el constructor resolvió el problema para mí. En mi caso específico, quería un tiempo de espera en milisegundos. Usando TimeSpan.FromMilliseconds(Configuration.HttpTimeout)en lugar de new TimeSpan(Configuration.HttpTimeout)trabajado un regalo. ¡Gracias!
Victor Ude
66
@RQDQ httpClient.Timeout = TimeSpan.FromMinutes(30)no es un buen enfoque, ya que bloqueará ese hilo en particular durante 30 minutos y tampoco alcanzará el punto final HTTP (que es su tarea principal). Además, si su programa finaliza antes de 30 minutos, es más probable que lo encuentre ThreadAbortException. Un mejor enfoque sería descubrir por qué ese punto final HTTP no está siendo afectado, podría requerir VPN o algún acceso restringido a la red.
Amit Upadhyay
66
@AmitUpadhyay Si la llamada se edita await, no se bloquea ningún hilo . No es el subproceso de la interfaz de usuario, no es un subproceso de grupo de subprocesos otro subproceso de fondo, ninguno.
Todd Menier
20

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

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

Ben Hutchison
fuente
9

Otra posibilidad es que el resultado no sea esperado en el lado del cliente. Esto puede suceder si alguno de los métodos de la pila de llamadas no utiliza la palabra clave wait para esperar a que se complete la llamada.

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

Lo anterior es el mejor enfoque para esperar en una solicitud grande. Estás confundido unos 30 minutos; es 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 minutos significa que el tiempo de procesamiento de la solicitud es de 30 minutos. Cuando se produjo el error "La tarea se canceló", o los requisitos de solicitud de datos de gran tamaño.

Navdeep Kapil
fuente
0

Otra razón puede ser que si está ejecutando el servicio (API) y coloca un punto de interrupción en el servicio (y su código está atascado en algún punto de interrupción (por ejemplo, la solución de Visual Studio muestra Depuración en lugar de Ejecutar )). y luego presionar la API desde el código del cliente. Entonces, si el código de servicio está en pausa en algún punto de interrupción, simplemente presione F5 en VS.

vivek nuna
fuente
0

En mi situación, el método del controlador no se hizo como asíncrono y el método llamado dentro del método del controlador fue asíncrono.

Así que supongo que es importante usar async / wait hasta el nivel superior para evitar problemas como estos.

chaitanyasingu
fuente