2013-09-05 16 views
5

Was will ich in meinem Programm realisieren, ist die folgende Aufrufliste/Workflow:Deadlock mit Asynchron & warten

  1. dispatch()
  2. autorisieren()
  3. Httppost()

Meine Idee war, dass httpPost() Async sein wird, während die anderen 2 Methoden nicht-async bleiben. Aber aus irgendeinem Grund würde es für mich nicht funktionieren, wenn ich nicht 2 + 3 machen würde. asynchron. Vielleicht habe ich noch ein paar Missverständnisse.

Zu meinem Verständnis kann ich entweder a) das await -keyword wenn die Async-Methode aufrufen (dies wird das Verfahren auszusetzen und auch weiterhin nach dem Asynchron-Methode) abgeschlossen ist, oder b) ommit die await -keyword und stattdessen Aufgabe nennen. Das Ergebnis der asynchronen Methoden gibt einen Wert zurück, der blockiert, bis das Ergebnis verfügbar ist.


Lassen Sie mich Ihnen das Arbeitsbeispiel:

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); 
    } 

Lassen Sie mich Ihnen die nicht -Arbeiten Beispiel:

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); 
    } 

Ich habe auch versucht, alle haben, 3 Methoden non-async und ersetzen die await s in httpPost mit .Result s, aber dann wird es für immer in der Linie hängen HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

Könnte jemand mich erleuchten und erklären, was mein Fehler ist?

Antwort

11

Sie haben eine SynchronizationContext, und dieser Kontext wird erfasst, wenn Sie await, so dass die Fortsetzung (en) in diesem Kontext ausgeführt werden können.

Sie fangen eine Asynchron-Aufgabe, ein continutation Planung in der Haupt Kontext zu einem späteren Zeitpunkt laufen.

Dann, bevor der Asynchron-Betrieb durchgeführt wird, haben Sie Code in Ihrem Haupt-Kontext eine blockierende Warte auf dem Asynchron-Betrieb zu tun. Die Fortsetzung kann nicht für die Ausführung geplant werden, da der Kontext auf die Fortsetzung wartet. Klassischer Deadlock.

Aus diesem Grunde ist es wichtig, „die Art und Weise all oben async“, wie Sie in Ihrem ersten Beispiel taten.

Es gibt ein paar Hacks, die rund um den Stillstand im zweiten Beispiel arbeiten können, aber es ist noch nichts, was man tun soll. Der gesamte Punkt des asynchronen Vorgangs besteht darin, den Thread (die Threads) nicht zu blockieren. Wenn Sie nur eine blockierende Warte auf die Aufgabe machen, vereiteln Sie den Zweck, asynchron zu gehen. Machen Sie alles asynchron oder nichts asynchron, es sei denn, Sie haben keine Wahl.

+0

Um zu testen, ob dies die richtige Erklärung ist, nehmen Sie den nicht-funktionierenden Code und ändert jeden „X erwarten“ auf „erwarten X.ConfigureAwait (false)“. Wenn die Erklärung richtig ist, sollte es jetzt funktionieren. Noch ein Hinweis: Sie haben eine SynchronizationContext, wenn Sie in ASP.Net oder in einer UI-Anwendung ausführen. Wenn ja, vergleichen Sie das Ausführen desselben Codes in einem eigenständigen Konsolenprojekt. – danarmak

+1

@servy: Was ich jedoch nicht verstehe, ist der tatsächliche Unterschied zwischen meinem Arbeitsbeispiel und dem nicht funktionierenden Beispiel. Ich verstehe, dass Sie sagen "erwarten" plant eine Fortsetzung während ".Result" einfach nur Blöcke (ist das so?), Aber würde dies nicht bedeuten, dass ich eine endlose async-erwarten-Kette brauchte, denn ohne 'erwarten 'würde ich blockieren/deadlock Mein Thema? Ich meine, das erste Beispiel ist * nicht * blockieren in 'int res = autorisieren (Optionen) .Result;', aber das zweite Beispiel blockiert in 'doSomethingWith (response.Result);', warum? – user826955