Ein asynchroner Aufruf mit Wartezeit in HttpClient wird nie zurückgegeben

95

Ich habe einen Anruf aus einer xaml-basierten C#Metro-Anwendung auf dem Win8 CP. Dieser Aufruf trifft einfach einen Webdienst und gibt JSON-Daten zurück.

HttpMessageHandler handler = new HttpClientHandler();

HttpClient httpClient = new HttpClient(handler);
httpClient.BaseAddress = new Uri("http://192.168.1.101/api/");

var result = await httpClient.GetStreamAsync("weeklyplan");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(WeeklyPlanData[]));
return (WeeklyPlanData[])ser.ReadObject(result);

Es hängt an der, awaitaber der http- Aufruf kehrt tatsächlich fast sofort zurück (bestätigt durch Fiddler); Es ist, als ob das awaitignoriert wird und es einfach dort hängt.

Bevor Sie fragen - JA - ist die Funktion "Privates Netzwerk" aktiviert.

Irgendwelche Ideen, warum das hängen würde?

keithwarren7
quelle
1
Wie nennt man diese asyncMethode? Wirft es keine Ausnahme?
Svick

Antworten:

136

Schauen Sie sich diese Antwort auf meine Frage an, die sehr ähnlich zu sein scheint.

Versuchen Sie Folgendes: Rufen Sie ConfigureAwait(false)die von zurückgegebene Aufgabe auf GetStreamAsync(). Z.B

var result = await httpClient.GetStreamAsync("weeklyplan")
                             .ConfigureAwait(continueOnCapturedContext:false);

Ob dies nützlich ist oder nicht, hängt davon ab, wie Ihr obiger Code aufgerufen wird. In meinem Fall hat das Aufrufen der asyncMethode mithilfe dazu geführt, Task.GetAwaiter().GetResult()dass der Code hängen geblieben ist.

Dies liegt daran GetResult(), dass der aktuelle Thread blockiert wird, bis die Aufgabe abgeschlossen ist. Wenn die Aufgabe abgeschlossen ist, versucht sie, den Thread-Kontext, in dem sie gestartet wurde, erneut einzugeben, kann dies jedoch nicht, da sich in diesem Kontext bereits ein Thread befindet, der durch den Aufruf von GetResult()... Deadlock blockiert wird !

In diesem MSDN-Beitrag wird ausführlich beschrieben, wie .NET parallele Threads synchronisiert. Die Antwort auf meine eigene Frage enthält einige bewährte Methoden.

Benjamin Fox
quelle
12
Danke, ich habe Async fast aufgegeben / warte, bevor ich das sehe.
Den
4
Ich auch! Warum ist dieses Zeug nicht besser dokumentiert?
Nochmals vielen
1
Wird es passieren, wenn es sich weder im UI-Kontext noch im ASP.NET-Kontext befindet?
Machinarium
1
Super Antwort! Ich bin jedoch verwirrt, warum ich dieses Problem bisher nur bei Verwendung von HttpClient habe. Es scheint, dass die zugrunde liegende Implementierung in HttpClient nicht korrekt implementiert ist. Die anderen Problemumgehungen, die ich gefunden habe, umfassen das Festlegen des aktuellen Threads als STA, was hilfreich ist, aber wirklich indirekt ist, insbesondere wenn Sie eine Assembly eines Drittanbieters verwenden und nicht wissen, dass unter der Haube ein Anruf definitiv auf eine Antwort warten wird wird nie bekommen. In meinem Fall war die DLL intern, sodass wir ConfigureAwait ausführen konnten ... aber dies musste auf der untersten Ebene des HttpClient-Objekts erfolgen.
Chris Schaller
2
@ChrisSchaller Lesen Sie unbedingt die vollständige Antwort unter stackoverflow.com/a/10351400/174735 , in der das Problem ziemlich vollständig erklärt wird.
Benjamin Fox
5

Nur ein Kopf hoch - wenn Sie das Warten auf der obersten Ebene in einem ASP.NET-Controller verpassen und die Aufgabe anstelle des Ergebnisses als Antwort zurückgeben, hängt sie tatsächlich nur fehlerfrei in den verschachtelten Warteanrufen. Ein dummer Fehler, aber hätte ich diesen Beitrag gesehen, hätte ich mir möglicherweise Zeit gespart, den Code auf etwas Seltsames zu überprüfen.

bozzle
quelle
0

Haftungsausschluss: Ich mag die ConfigureAwait () - Lösung nicht, weil ich sie nicht intuitiv und schwer zu merken finde. Stattdessen kam ich zu dem Schluss, nicht erwartete Methodenaufrufe in Task.Run (() => myAsyncMethodNotUsingAwait ()) zu verpacken. Dies scheint zu 100% zu funktionieren, könnte aber nur eine Rennbedingung sein!? Ich bin mir nicht so sicher, was ehrlich sein wird. Diese Schlussfolgerung könnte falsch sein und ich riskiere meine StackOverflow-Punkte hier, um hoffentlich aus den Kommentaren zu lernen :-P. Bitte lesen Sie sie!

Ich hatte gerade das Problem , wie beschrieben und weitere Informationen finden Sie hier .

Die Aussage lautet: "Sie können keine asynchrone Methode aufrufen"

await asyncmethod2()

von einer Methode, die blockiert

myAsyncMethod().Result

In meinem Fall konnte ich die aufrufende Methode nicht ändern und sie war nicht asynchron. Aber das Ergebnis war mir eigentlich egal. Soweit ich mich erinnere, hat es auch nicht funktioniert, das .Result zu entfernen und das Warten zu verpassen.

Also habe ich das gemacht:

public void Configure()
{
    var data = "my data";
    Task.Run(() => NotifyApi(data));
}

private async Task NotifyApi(bool data)
{
    var toSend = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
    await client.PostAsync("http://...", data);
}

In meinem Fall war mir das Ergebnis der aufrufenden nicht-asynchronen Methode egal, aber ich denke, das ist in diesem Anwendungsfall ziemlich häufig. Sie können das Ergebnis in der aufrufenden asynchronen Methode verwenden.

CodingYourLife
quelle