c# .net timeout task-parallel-library dotnet-httpclient

c# - Distinguir el tiempo de espera de la cancelación del usuario



.net timeout (2)

La respuesta aceptada es ciertamente cómo debería funcionar esto en teoría, pero desafortunadamente en la práctica, IsCancellationRequested no se establece (de manera confiable) en el token que se adjunta a la excepción:

Cancelación de una solicitud de HttpClient: ¿Por qué es TaskCanceledException.CancellationToken.IsCancellationRequested false?

HttpClient tiene una función de tiempo de espera incorporada (a pesar de ser asincrónica, es decir, los tiempos de espera podrían considerarse ortogonales a la funcionalidad de solicitud de http y, por lo tanto, ser manejados por utilidades asíncronas genéricas, pero aparte de eso). envuelto en un AggregateException ).

El TCE contiene un CancellationToken que equivale a un CancellationToken.None .

Ahora, si proporciono a HttpClient mi propio CancellationToken y lo utilizo para cancelar la operación antes de que finalice (o se agote el tiempo de espera), obtengo exactamente la misma TaskCanceledException , nuevamente con una CancellationToken.None TaskCanceledException .

¿Todavía hay una manera, al ver solo la excepción lanzada , para averiguar si un tiempo de espera canceló la solicitud, sin tener que hacer que mi propio CancellationToken accesible al código que verifica la excepción?

PD: ¿Podría tratarse de un error y CancellationToken se corrigió erróneamente en CancellationToken.None ? En el caso cancelado utilizando el caso de TaskCanceledException.CancellationToken personalizado , esperaría que TaskCanceledException.CancellationToken igual a ese token personalizado.

Editar Para aclarar un poco más el problema, con el acceso a la CancellationTokenSource original, es fácil distinguir el tiempo de espera y la cancelación del usuario:

origCancellationTokenSource.IsCancellationRequested == true

Sin embargo, obtener la CancellationToken de la excepción da una respuesta incorrecta:

((TaskCanceledException) e.InnerException) .CancellationToken.IsCancellationRequested == false

Aquí un ejemplo mínimo , debido a la demanda popular:

public void foo() { makeRequest().ContinueWith(task => { try { var result = task.Result; // do something with the result; } catch (Exception e) { TaskCanceledException innerException = e.InnerException as TaskCanceledException; bool timedOut = innerException != null && innerException.CancellationToken.IsCancellationRequested == false; // Unfortunately, the above .IsCancellationRequested // is always false, no matter if the request was // cancelled using CancellationTaskSource.Cancel() // or if it timed out } }); } public Task<HttpResponseMessage> makeRequest() { var cts = new CancellationTokenSource(); HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10) }; HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "url"); passCancellationTokenToOtherPartOfTheCode(cts); return client.SendAsync(httpRequestMessage, cts.Token); }


Sí, ambos devuelven la misma excepción (posiblemente debido a un tiempo de espera interno que usa un token también) pero se puede resolver fácilmente haciendo esto:

catch (OperationCanceledException ex) { if (token.IsCancellationRequested) { return -1; } return -2; }

así que básicamente si golpeas la excepción pero tu token no se cancela, bueno, fue un tiempo de espera de http regular