HttpClient - zadanie zostało anulowane?

191

Działa dobrze, gdy ma jedno lub dwa zadania, ale zgłasza błąd „Zadanie zostało anulowane”, gdy mamy na liście więcej niż jedno zadanie.

wprowadź opis zdjęcia tutaj

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
źródło
Co mówi wewnętrzny wyjątek?
RagtimeWilly
1
Dlaczego bierzesz CancellationTokenjako parametr i nie używasz go?
Jim Aho
1
Powodem dla mnie było przypadkowe usunięcie HttpClient, np.async Task<HttpResponseMessage> Method(){ using(var client = new HttpClient()) return client.GetAsync(request); }
JobaDiniz
3
Dla tych, którzy używają HttpClientjak @JobaDiniz (z a using()), przestań! Powód: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Philippe

Odpowiedzi:

274

Istnieją 2 prawdopodobne powody, dla TaskCanceledExceptionktórych zostanie rzucony:

  1. Coś, co nazywa Cancel()naCancellationTokenSource związane z odwołaniem tokena przed zadanie zakończone.
  2. Upłynął limit czasu żądania, tzn. Nie zakończył się w określonym przez Ciebie przedziale czasowym HttpClient.Timeout.

Domyślam się, że upłynął limit czasu. (Gdyby było to wyraźne anulowanie, prawdopodobnie zorientowałbyś się.) Możesz być bardziej pewien, sprawdzając wyjątek:

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
źródło
3
Jakie jest możliwe rozwiązanie? Mam podobny problem. stackoverflow.com/questions/36937328/…
Deweloper
49
@Dimi - jest dość stary, ale rozwiązaniem, którego użyłem, było ustawienie właściwości Timeout na większą wartość:httpClient.Timeout = TimeSpan.FromMinutes(30)
RQDQ
2
@RQDQ, ty mężczyzna, mężczyzna! Nieużywanie konstruktora rozwiązało problem. W moim konkretnym przypadku chciałem przekroczyć limit czasu w milisekundach. Używanie TimeSpan.FromMilliseconds(Configuration.HttpTimeout)zamiast new TimeSpan(Configuration.HttpTimeout)pracy. Dzięki!
Victor Ude,
6
@RQDQ httpClient.Timeout = TimeSpan.FromMinutes(30)nie jest dobrym podejściem, ponieważ zablokuje ten konkretny wątek na 30 minut, a także nie trafi do punktu końcowego HTTP (co jest twoim głównym zadaniem). Ponadto, jeśli Twój program zakończy się przed 30 minutami, najprawdopodobniej napotkasz ThreadAbortException. Lepszym rozwiązaniem byłoby ustalenie, dlaczego ten punkt końcowy HTTP nie został trafiony, może wymagać VPN lub jakiegoś ograniczonego dostępu do sieci.
Amit Upadhyay
6
@AmitUpadhyay Jeśli połączenie jest edytowane await, żaden wątek nie jest blokowany. Nie wątek interfejsu użytkownika, nie wątek puli wątków inny wątek tła, brak.
Todd Menier
20

Natknąłem się na ten problem, ponieważ moja Main()metoda nie czekała na zakończenie zadania przed powrotem, więc Task<HttpResponseMessage> myTaskzostała anulowana po wyjściu z programu konsoli.

Rozwiązaniem było zadzwonić myTask.GetAwaiter().GetResult()w Main()(z tej odpowiedzi ).

Ben Hutchison
źródło
9

Inną możliwością jest to, że wynik nie jest oczekiwany po stronie klienta. Może się to zdarzyć, jeśli jakakolwiek metoda na stosie wywołań nie używa słowa kluczowego czekania do oczekiwania na zakończenie połączenia.

Manish
źródło
8
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);

Powyższe jest najlepszym podejściem do oczekiwania na duże zamówienie. Jesteś zdezorientowany około 30 minut; jest to losowy czas i możesz dać dowolny czas, który chcesz.

Innymi słowy, żądanie nie będzie czekać przez 30 minut, jeśli otrzyma wyniki przed 30 minutami. 30 minut oznacza, że ​​czas przetwarzania żądania wynosi 30 minut. Kiedy wystąpił błąd „Zadanie zostało anulowane” lub wymagania dotyczące dużych danych.

Navdeep Kapil
źródło
0

Innym powodem może być to, że jeśli uruchomisz usługę (API) i umieścisz punkt przerwania w usłudze (a kod utknie w pewnym punkcie przerwania (np. Rozwiązanie Visual Studio pokazuje Debugowanie zamiast Uruchamianie )). a następnie kliknięcie interfejsu API z kodu klienta. Więc jeśli kod usługi został wstrzymany w pewnym punkcie przerwania, po prostu nacisnąłeś F5 w VS.

vivek nuna
źródło
0

W mojej sytuacji metoda kontrolera nie została wykonana jako asynchroniczna, a metoda wywołana w metodzie kontrolera była asynchroniczna.

Sądzę więc, że ważne jest używanie asynchronizacji / czekania aż do najwyższego poziomu, aby uniknąć takich problemów.

chaitanyasingu
źródło