web-dev-qa-db-ja.com

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

XamlベースのC# Win8 CPのメトロアプリケーション。この呼び出しは、単に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が無視され、そこにハングするかのようです。

尋ねる前に-YES-プライベートネットワーク機能がオンになります。

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

88
keithwarren7

この回答 よく似ていると思われる私の質問をご覧ください。

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

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

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

これは、GetResult()がタスクが完了するまで現在のスレッドをブロックするためです。タスクが完了すると、タスクは開始されたスレッドコンテキストへの再入力を試みますが、そのコンテキストに既にスレッドが存在するため、GetResult()...デッドロック!

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

122
Benjamin Fox

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

2
bozzle

免責事項: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);
}

私の場合、非同期メソッドの呼び出しの結果は気にしませんでしたが、このユースケースでは非常に一般的だと思います。結果を呼び出し非同期メソッドで使用できます。

0
CodingYourLife