web-dev-qa-db-fra.com

L'appel asynchrone avec wait dans HttpClient ne revient jamais

J'ai un appel que je passe depuis un ordinateur basé sur xaml, C# application de métro sur le Win8 CP; cet appel frappe simplement un service Web et renvoie des données 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);

Il se bloque au await mais l'appel http revient presque immédiatement (confirmé par le violoneur); c'est comme si le await était ignoré et qu'il se bloquait là.

Avant de vous demander - OUI - la capacité de réseau privé est activée.

Des idées pourquoi cela pendre?

88
keithwarren7

Départ cette réponse à ma question qui semble être très similaire.

Quelque chose à essayer: appelez ConfigureAwait(false) sur la tâche renvoyée par GetStreamAsync(). Par exemple.

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

Que cela soit utile ou non dépend de la façon dont votre code ci-dessus est appelé. Dans mon cas, appeler la méthode async à l'aide de Task.GetAwaiter().GetResult() a provoqué le blocage du code.

En effet, GetResult() bloque le thread actuel jusqu'à la fin de la tâche. Une fois la tâche terminée, elle tente de rentrer dans le contexte du thread dans lequel elle a été lancée mais ne le peut pas car il existe déjà un thread dans ce contexte, qui est bloqué par l'appel de GetResult()... impasse!

Cet article MSDN explique en détail la façon dont .NET synchronise les threads parallèles - et la réponse à ma propre question donne certaines pratiques recommandées.

122
Benjamin Fox

Juste un avertissement - si vous manquez l'attente au plus haut niveau dans un contrôleur ASP.NET et que vous renvoyez la tâche au lieu du résultat, elle sera simplement suspendue dans le ou les appels imbriqués imbriqués sans erreur. Une erreur stupide, mais si j'avais vu ce post, cela m'aurait peut-être fait gagner du temps en vérifiant le code pour trouver quelque chose d'étrange.

2
bozzle

Clause de non-responsabilité: je n'aime pas la solution ConfigureAwait () car je la trouve non intuitive et difficile à retenir. Au lieu de cela, je suis parvenu à la conclusion d'envelopper des appels de méthode non attendus dans Task.Run () => myAsyncMethodNotUsingAwait ()). Cela semble fonctionner à 100%, mais pourrait être juste une condition de concurrence!? Pour être honnête, je ne suis pas sûr de ce qui se passe. Cette conclusion pourrait être fausse et je risque ici mes points StackOverflow pour apprendre, espérons-le, des commentaires :-P. S'il vous plaît, lisez-les!

Je viens d'avoir le problème comme décrit et trouvé plus d'informations ici .

L'instruction est: "vous ne pouvez pas appeler une méthode asynchrone"

await asyncmethod2()

à partir d'une méthode qui bloque

myAsyncMethod().Result

Dans mon cas, je ne pouvais pas changer la méthode d'appel et ce n'était pas asynchrone. Mais le résultat ne m'intéressait pas vraiment. Si je me souviens bien, cela ne fonctionnait pas non plus, supprimer le résultat. Il manquait l'attente.

Alors j'ai fait ça:

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

Dans mon cas, le résultat dans la méthode d'appel non-asynchrone me importait peu, mais je suppose que c'est assez courant dans ce cas d'utilisation. Vous pouvez utiliser le résultat dans la méthode asynchrone appelante.

0
CodingYourLife