私は、C# async
/await
キーワードを使用した非同期プログラミングのベスト プラクティスをいくつか見つけました (私は C# 5.0 を初めて使用します)。
与えられたアドバイスの 1 つは次のとおりでした。
安定性: 同期コンテキストを把握する
... 一部の同期コンテキストは再入不可でシングル スレッドです。つまり、特定の時点でコンテキスト内で実行できる作業単位は 1 つだけです。この例としては、Windows UI スレッドや ASP.NET 要求コンテキストがあります。これらのシングル スレッド同期コンテキストでは、デッドロックに陥りやすくなります。シングル スレッド コンテキストからタスクを生成し、コンテキスト内でそのタスクを待機すると、待機中のコードがバックグラウンド タスクをブロックする可能性があります。
public ActionResult ActionAsync()
{
// DEADLOCK: this blocks on the async task
var data = GetDataAsync().Result;
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
自分で分析してみると、メイン スレッドは で新しいスレッドを生成しますMyWebService.GetDataAsync();
が、メイン スレッドはそこで待機しているため、 の結果を待機しますGetDataAsync().Result
。その間に、データが準備できたとします。メイン スレッドが継続ロジックを続行せず、 から文字列の結果を返さないのはなぜでしょうかGetDataAsync()
。
上記の例でデッドロックが発生する理由を誰か説明してもらえますか? 何が問題なのか全くわかりません...
ベストアンサー1
を見てみましょうこの例スティーブンはあなたに明確な答えを持っています:
したがって、トップレベルのメソッド (
Button1_Click
UI の場合 /MyController.Get
ASP.NET の場合) から始めると、次のようになります。
トップレベルのメソッド呼び出し
GetJsonAsync
(UI/ASP.NET コンテキスト内)。
GetJsonAsync
HttpClient.GetStringAsync
呼び出して(コンテキスト内で) REST リクエストを開始します。
GetStringAsync
Task
REST リクエストが完了していないことを示すuncompleted を返します。
GetJsonAsync
Task
によって返される を待機しますGetStringAsync
。コンテキストはキャプチャされ、GetJsonAsync
後でメソッドの実行を続行するために使用されます。は、メソッドが完了していないことを示す、GetJsonAsync
未完了の を返します。Task
GetJsonAsync
トップレベルのメソッドは、
Task
によって返されたを同期的にブロックしますGetJsonAsync
。これにより、コンテキスト スレッドがブロックされます。... 最終的に、REST リクエストが完了します。これ
Task
により、 によって返された が完了しますGetStringAsync
。継続の実行
GetJsonAsync
準備が整い、コンテキストが使用可能になるまで待機して、コンテキスト内で実行できるようになります。デッドロック最上位のメソッドはコンテキスト スレッドをブロックし、
GetJsonAsync
完了を待機し、GetJsonAsync
完了できるようにコンテキストが解放されるのを待機しています。UI の例では、「コンテキスト」は UI コンテキストです。ASP.NET の例では、「コンテキスト」は ASP.NET 要求コンテキストです。このタイプのデッドロックは、どちらの「コンテキスト」でも発生する可能性があります。
もう一つ読んでおくべきリンク:Await、UI、そしてデッドロック! ああ、大変!