HttpClientでawaitを使用した非同期呼び出しが返されない


95

C#Win8 CPのxaml ベースのMetroアプリケーションの内部から電話をかけています。この呼び出しは単にWebサービスをヒットし、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);

これはでハングしますawaitが、http呼び出しは実際にはほとんどすぐに戻ります(フィドラーで確認)。これはawait無視されたかのようにハングアップするだけです。

質問する前に -はい-プライベートネットワーク機能がオンになっています。

これがハングする理由はありますか?


1
そのasyncメソッドをどのように呼び出していますか?例外を投げませんか?
スビック

回答:


136

非常に似ていると思われる私の質問に対するこの回答を確認してください。

試してみる:からConfigureAwait(false)返されたタスクを呼び出しますGetStreamAsync()。例えば

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

これが有用かどうかは、上記のコードがどのように呼び出されているかによって異なります。私の場合、async使用するメソッドを呼び出すTask.GetAwaiter().GetResult()と、コードがハングしました。

これはGetResult()、タスクが完了するまで現在のスレッドをブロックするためです。タスクが完了すると、タスクが開始されたスレッドコンテキストに再入しようとしますが、そのコンテキストにはすでにスレッドがあるため、それはできません。スレッドはGetResult()... デッドロックへの呼び出しによってブロックされています

このMSDNの投稿では、.NETが並列スレッドを同期する方法について少し詳しく説明しています。また、私の質問に対する回答は、いくつかのベストプラクティスを提供します。


12
おかげで、これを見る前にasync / awaitをほとんどあきらめました。
Den

4
私も!なぜこれはもっと文書化されていないのですか?ありがとうございました
Avrohom Yisroel 14年

1
UIコンテキストとASP.NETコンテキストのどちらにもない場合に発生しますか?
machinarium

1
素晴らしい答えです!しかし、これまでのところHttpClientを使用するときにのみこの問題が発生するのはなぜかと混乱しています。HttpClient内の基本的な実装が正しく実装されていないようです。私が見つけた他の回避策には、現在のスレッドをSTAとして設定することが含まれますが、これは役立ちますが、特にサードパーティのアセンブリを使用していて、内部で一部の呼び出しがその応答を確実に待機することに気付いていない場合は特に間接的です決して得るつもりはない。私の場合、dllは社内にあったため、ConfigureAwaitを実行できましたが、HttpClient objの最低レベルで実行する必要がありました。
Chris Schaller

2
@ChrisSchaller 問題をかなり完全に説明しているstackoverflow.com/a/10351400/174735の完全な回答を必ずお読みください。
ベンジャミンフォックス

5

ただのヘッドアップ-ASP.NETコントローラーのトップレベルでの待機を逃し、結果として応答ではなくタスクを返す場合、エラーなしでネストされた待機呼び出しでハングします。ばかげた間違いですが、この投稿を見ていれば、コードをチェックして何かおかしなことをする時間を節約できたかもしれません。


0

免責事項:ConfigureAwait()ソリューションは直感的でなく覚えにくいため、私は好きではありません。代わりに、私はTask.Run(()=> myAsyncMethodNotUsingAwait())で待機していないメソッド呼び出しをラップするという結論に達しました。これは100%動作するように見えますが、単に競合状態かもしれません!?正直なところ何が起こっているのかよくわかりません。この結論は間違っている可能性があり、私はここに私のStackOverflowポイントを危険にさらしてコメントから学ぶことができれば幸いです:-P。ぜひお読みください!

説明したとおりに問題が発生し、詳細はこちらで見つかりまし

ステートメントは次のとおりです。「非同期メソッドを呼び出すことはできません」

await asyncmethod2()

ブロックするメソッドから

myAsyncMethod().Result

私の場合、呼び出しメソッドを変更できず、非同期ではありませんでした。しかし、私は実際には結果を気にしませんでした。私が覚えているように、それは.Resultを削除しても機能せず、待機がありません。

だから私はこれをしました:

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

私の場合、非同期ではないメソッドの呼び出しの結果については気にしていませんでしたが、この使用例ではそれが一般的であると思います。結果は呼び出し元の非同期メソッドで使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.