La chiamata asincrona con await in HttpClient non viene mai restituita


95

Ho una chiamata che sto effettuando dall'interno di C#un'applicazione metro basata su xaml su Win8 CP; questa chiamata raggiunge semplicemente un servizio Web e restituisce dati JSON.

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

Si blocca al awaitma la chiamata http effettivamente ritorna quasi immediatamente (confermato tramite fiddler); è come se awaitfosse ignorato e rimanesse semplicemente lì.

Prima di chiedere - SÌ - la funzionalità di rete privata è attivata.

Qualche idea sul perché questo si bloccherebbe?


1
Come chiami quel asyncmetodo? Non genera un'eccezione?
svick

Risposte:


136

Dai un'occhiata a questa risposta alla mia domanda che sembra essere molto simile.

Qualcosa da provare: chiama ConfigureAwait(false)l'attività restituita da GetStreamAsync(). Per esempio

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

L'utilità o meno dipende da come viene chiamato il codice sopra - nel mio caso la chiamata al asyncmetodo using ha Task.GetAwaiter().GetResult()causato il blocco del codice.

Questo perché GetResult()blocca il thread corrente fino al completamento dell'attività. Quando l'attività viene completata, tenta di rientrare nel contesto del thread in cui è stata avviata ma non può perché in quel contesto è già presente un thread, che è bloccato dalla chiamata a GetResult()... deadlock!

Questo post su MSDN fornisce alcuni dettagli su come .NET sincronizza i thread paralleli e la risposta data alla mia domanda fornisce alcune best practice.


12
Grazie, ho quasi rinunciato ad async / attendere prima di vedere questo.
Den

4
Anch'io! Perché questa roba non è documentata meglio? Grazie ancora
Avrohom Yisroel

1
Accadrà se non si trova né nel contesto dell'interfaccia utente né nel contesto ASP.NET?
machinarium

1
Risposta fantastica! Ma sono confuso perché finora ho questo problema solo quando si utilizza HttpClient, sembra che l'implementazione sottostante all'interno di HttpClient non sia implementata correttamente. Le altre soluzioni alternative che ho trovato riguardano l'impostazione del thread corrente come STA che aiuta ma è davvero indiretto soprattutto quando si utilizza un assembly di terze parti e non si è consapevoli che sotto il cofano alcune chiamate aspetteranno sicuramente una risposta che non arriverà mai. Nel mio caso la dll era in-house, quindi siamo stati in grado di ConfigureAwait ... ma doveva essere fatto al livello più basso dell'obj HttpClient.
Chris Schaller

2
@ChrisSchaller Assicurati di leggere la risposta completa su stackoverflow.com/a/10351400/174735 , che spiega il problema in modo abbastanza completo.
Benjamin Fox

5

Solo un avvertimento: se perdi l'attesa al livello più alto in un controller ASP.NET e restituisci l'attività anziché il risultato come risposta, in realtà si blocca nelle chiamate di attesa annidate senza errori. Uno stupido errore, ma se avessi visto questo post mi avrebbe fatto risparmiare un po 'di tempo a controllare il codice per qualcosa di strano.


0

Dichiarazione di non responsabilità: non mi piace la soluzione ConfigureAwait () perché la trovo non intuitiva e difficile da ricordare. Invece sono giunto alla conclusione di racchiudere le chiamate di metodo non attese in Task.Run (() => myAsyncMethodNotUsingAwait ()). Sembra funzionare al 100% ma potrebbe essere solo una condizione di gara !? Non sono così sicuro di cosa stia succedendo ad essere onesto. Questa conclusione potrebbe essere sbagliata e rischio i miei punti StackOverflow qui per imparare, si spera, dai commenti :-P. Per favore, leggili!

Ho appena avuto il problema come descritto e ho trovato maggiori informazioni qui .

L'affermazione è: "non puoi chiamare un metodo asincrono"

await asyncmethod2()

da un metodo che blocca

myAsyncMethod().Result

Nel mio caso non ho potuto cambiare il metodo di chiamata e non era asincrono. Ma in realtà non mi importava del risultato. Come ricordo, inoltre, non ha funzionato rimuovendo il .Result e ho l'attesa mancante.

Quindi ho fatto questo:

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

Nel mio caso non mi importava del risultato nella chiamata del metodo non asincrono, ma immagino che sia abbastanza comune in questo caso d'uso. È possibile utilizzare il risultato nel metodo asincrono chiamante.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.