2013-09-05 13 views
5

Co chcę zrealizować w moim programie jest następujący callstack/Workflow:Deadlock użyciu asynchronicznie i czekają

  1. dispatch()
  2. autoryzacji()
  3. HttpPost()

Mój pomysł był taki, że httpPost() będzie asynchronizowany, podczas gdy pozostałe 2 metody pozostaną niezsynchronizowane. Jednak z jakiegoś powodu nie zadziałałoby to dla mnie, chyba że zrobię 2 + 3. async. Może nadal mam pewne nieporozumienia.

mojego rozeznania mogę albo a) używać await -słowo kluczowe podczas wywoływania metody asynchronicznej (ten zawiesi metody i kontynuować po zakończeniu sposób asynchroniczny) lub b) ommit await -słowo kluczowe i zamiast zadzwonić zadanie. Wynik metody asynchronicznej zwraca wartość, która będzie blokowana, dopóki wynik nie będzie dostępny.


Pokażę wam przykład robocza:

private int dispatch(string options) 
    { 
     int res = authorize(options).Result; 
     return res; 
    } 

    static async private Task<int> authorize(string options) 
    { 
     string values= getValuesFromOptions(options); 

     KeyValuePair<int, string> response = await httpPost(url, values); 

     return 0; 
    } 

    public static async Task<KeyValuePair<int, string>> httpPost(string url, List<KeyValuePair<string, string>> parameters) 
    { 
    var httpClient = new HttpClient(new HttpClientHandler()); 

    HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)); 

    int code = (int)response.StatusCode; 

    response.EnsureSuccessStatusCode(); 

    string responseString = await response.Content.ReadAsStringAsync(); 

    return new KeyValuePair<int, string>(code, responseString); 
    } 

Pokażę wam przykład non -Praca:

private int dispatch(string options) 
    { 
     int res = authorize(options).Result; 
     return res; 
    } 

    static private int authorize(string options) 
    { 
     string values= getValuesFromOptions(options); 

     Task<KeyValuePair<int, string>> response = httpPost(url, values); 

     doSomethingWith(response.Result); // execution will hang here forever 

     return 0; 
    } 

    public static async Task<KeyValuePair<int, string>> httpPost(string url, List<KeyValuePair<string, string>> parameters) 
    { 
    var httpClient = new HttpClient(new HttpClientHandler()); 

    HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)); 

    int code = (int)response.StatusCode; 

    response.EnsureSuccessStatusCode(); 

    string responseString = await response.Content.ReadAsStringAsync(); 

    return new KeyValuePair<int, string>(code, responseString); 
    } 

Próbowałem też mieć wszystko 3 metody niezsynchronizowane i zastępujące await s w await s w await s z .Result s, ale potem będzie wisiał na zawsze w linii HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

Czy ktoś mógłby mnie oświecić i wyjaśnić, co mój błąd?

Odpowiedz

11

Masz SynchronizationContext, a kontekst jest przechwytywany, gdy await, aby kontynuacja (s) mogła działać w tym kontekście.

Rozpoczynasz zadanie asynchroniczne, planując kontynuację działania w twoim głównym kontekście w pewnym późniejszym momencie.

Następnie, przed wykonaniem operacji asynchronicznej, w głównym kontekście znajduje się kod powodujący blokowanie oczekiwania na operację asynchroniczną. Nie można zaplanować kontynuacji, ponieważ kontekst jest zajęty oczekiwaniem na kontynuację. Klasyczny impas.

Dlatego tak ważne jest, aby "asynchronicznie podnieść się", tak jak w pierwszym przykładzie.

Jest kilka hacków, które mogą obejść zakleszczenie w drugim przykładzie, ale nadal nie jest to coś, co powinieneś robić. Cały punkt asynchroniczny polega na uniknięciu blokowania wątków. Jeśli po prostu zaczniesz blokować oczekiwanie na zadanie, i tak pokonasz cel asynchroniczny. Albo spraw, aby wszystko było asynchroniczne, albo nic asynchronicznego, chyba że nie masz wyboru.

+0

Aby sprawdzić, czy jest to poprawne wyjaśnienie, weź niedziałający kod i zmień każde "oczekuj X" na "czekać na X.ConfigureAwait (fałsz)". Jeśli wyjaśnienie jest poprawne, powinno teraz działać. Kolejna uwaga: masz kontekst synchronizacji, jeśli używasz ASP.Net lub aplikacji interfejsu użytkownika. Jeśli tak, porównaj ten sam kod w samodzielnym projekcie konsoli. – danarmak

+1

@servy: To, czego nie dostaję, to jednak faktyczna różnica pomiędzy moim przykładem roboczym a niedziałającym przykładem. Rozumiem, że mówisz, że 'czekaj' ma harmonogram kontynuacji, podczas gdy' .Rekult' po prostu blokuje (czy tak?), Ale nie znaczyłoby to, że potrzebowałem niekończącego się łańcucha oczekującego na oczekiwanie, ponieważ bez 'wyczekiwania 'blokowałbym/zakleszczenie mój wątek? Mam na myśli pierwszy przykład robi * nie * blok w 'int res = authorize (options) .Result;', ale drugi przykład blokuje w 'doSomethingWith (response.Result);', dlaczego? – user826955

Powiązane problemy