HttpClient - Une tâche a été annulée?

191

Cela fonctionne bien quand avoir une ou deux tâches génère une erreur "Une tâche a été annulée" lorsque nous avons plus d'une tâche répertoriée.

entrez la description de l'image ici

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
la source
Que dit l'exception intérieure?
RagtimeWilly
1
Pourquoi prenez-vous un CancellationTokencomme paramètre et ne l'utilisez pas?
Jim Aho
1
La raison pour moi était de disposer HttpClientpar erreur, par exempleasync Task<HttpResponseMessage> Method(){ using(var client = new HttpClient()) return client.GetAsync(request); }
JobaDiniz
3
Pour ceux qui utilisent HttpClientcomme @JobaDiniz (avec un using()), veuillez arrêter! La raison: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Philippe

Réponses:

274

Il y a 2 raisons probables pour lesquelles un TaskCanceledExceptionserait jeté:

  1. Quelque chose a appelé Cancel()le CancellationTokenSourceassocié au jeton d'annulation avant la fin de la tâche.
  2. La demande a expiré, c'est-à-dire qu'elle n'a pas été exécutée dans le délai spécifié HttpClient.Timeout.

Je suppose que c'était un temps mort. (S'il s'agissait d'une annulation explicite, vous l'auriez probablement compris.) Vous pouvez être plus certain en inspectant l'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.
}
Todd Menier
la source
3
Alors, quelle est une solution possible? J'ai un problème similaire. stackoverflow.com/questions/36937328/…
Développeur
49
@Dimi - c'est assez vieux, mais la solution que j'ai utilisée était de définir la propriété Timeout sur une valeur plus grande:httpClient.Timeout = TimeSpan.FromMinutes(30)
RQDQ
2
@RQDQ, vous l'homme, l'homme! Ne pas utiliser le constructeur a résolu le problème pour moi. Dans mon cas particulier, je voulais un délai d'attente en millisecondes. Utiliser TimeSpan.FromMilliseconds(Configuration.HttpTimeout)plutôt que new TimeSpan(Configuration.HttpTimeout)travailler un régal. Merci!
Victor Ude
6
@RQDQ httpClient.Timeout = TimeSpan.FromMinutes(30)n'est pas une bonne approche, car il bloquera ce thread particulier pendant 30 minutes et n'atteindra pas non plus le point de terminaison HTTP (qui est votre tâche principale). De plus, si votre programme se termine avant 30 minutes, vous êtes plus susceptible de rencontrer ThreadAbortException. Une meilleure approche serait de découvrir pourquoi ce point de terminaison HTTP n'est pas touché, il peut nécessiter un VPN ou un accès réseau restreint.
Amit Upadhyay
6
@AmitUpadhyay Si l'appel est émis await, aucun thread n'est bloqué. Pas le thread d'interface utilisateur, pas un thread de threadpool autre thread d'arrière-plan, aucun.
Todd Menier
20

J'ai rencontré ce problème car ma Main()méthode n'attendait pas que la tâche soit terminée avant de revenir, donc le Task<HttpResponseMessage> myTaskétait en cours d'annulation lorsque mon programme de console s'est arrêté.

La solution était d'appeler myTask.GetAwaiter().GetResult()à Main()(de cette réponse ).

Ben Hutchison
la source
9

Une autre possibilité est que le résultat ne soit pas attendu côté client. Cela peut se produire si l'une des méthodes de la pile d'appels n'utilise pas le mot clé await pour attendre la fin de l'appel.

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

Ce qui précède est la meilleure approche pour attendre une demande importante. Vous êtes confus environ 30 minutes; c'est un temps aléatoire et vous pouvez donner le temps que vous voulez.

En d'autres termes, la requête n'attendra pas 30 minutes si elle obtient des résultats avant 30 minutes. 30 min signifie que le temps de traitement de la demande est de 30 min. Lorsque nous nous sommes produits, l'erreur «La tâche a été annulée» ou les exigences de demande de données volumineuses.

Navdeep Kapil
la source
0

Une autre raison peut être que si vous exécutez le service (API) et mettez un point d'arrêt dans le service (et que votre code est bloqué à un point d'arrêt (par exemple, la solution Visual Studio affiche le débogage au lieu de l' exécution )). puis frapper l'API à partir du code client. Donc, si le code de service s'est interrompu sur un point d'arrêt, vous appuyez simplement sur F5 dans VS.

vivek nuna
la source
0

Dans ma situation, la méthode du contrôleur n'était pas asynchrone et la méthode appelée à l'intérieur de la méthode du contrôleur était async.

Je suppose donc qu'il est important d'utiliser async / await jusqu'au niveau supérieur pour éviter des problèmes comme ceux-ci.

chaitanyasingu
la source