C#async awaitデッドロック問題のまとめ
8458 ワード
デッドロックが発生する可能性のあるプログラムタイプ
1、WPF/WinFormプログラム
2、asp.Net(asp.net mvcを除く)プログラム
デッドロックの発生原理
非同期メソッドで返されるTaskに対してWait()を呼び出したりResultプロパティにアクセスしたりすると、デッドロックが発生する可能性があります.
次のWPFコードでデッドロックが発生します.
次のasp.Netmvcコードにもデッドロックが発生します.
WPFコードの例として、イベントプロセッサはMethod 1を呼び出し、Taskオブジェクトを取得し、TaskのWaitメソッドを呼び出し、Taskオブジェクトが「完了」するまで、自分の存在するスレッド、すなわちメインスレッドをブロックする.戻ってきたTaskオブジェクトを「完了」するには、メインスレッド上でawait後のコードを実行する必要があります.メインスレッドはすでにブロックされており、Taskオブジェクトの完了を待っています.するとデッドロックが発生します.
asp.Netmvcコードは同じ理屈です.
デッドロックを回避する方法
次のコードは問題ありません.
次のコードにも問題はありません.
下にはデッドロックが発生します.
なぜHttpClientのGetStringAsync()から返されたTaskでResutにアクセスしてデッドロックは発生せず、自分が書いたコードでデッドロックが発生したのですか?
monoのHttpClientソースコードから、いくつかのヒントを見つけることができます.
すべてのawait式の後に、ConfigureAwait(false)が追加されています.
return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
Taskのmsdnドキュメントから分かるように、ConfigureAwait(false)はawait以降のコードが元のcontext(スレッドと理解できる)上で実行されないことを示す.
これにより,最初のWPFコードを例にとると,メインスレッドがブロックされ,Taskオブジェクトの「完了」を待つ.Method 1が100 ms呼び出された後、別のスレッドでawaitの後のコードが実行され、Taskオブジェクトが完了する.メインスレッドは実行を再開します.
HttpClientを使用してデッドロックを起こすコードを以下の形式に変更します.
デッドロックは現れないことがわかります
について述べる非同期ツールメソッドでは、可能な限りConfigureAwait(false)を加えることで、メソッドの利用者が非同期メソッドから戻ってきたTask上でWait()を呼び出したりResult属性にアクセスしたりしてデッドロック を引き起こすことを防止することができる.いくつかのシーンでは、awaitの後のコードを元のcontextに戻す必要があります.たとえば、awaitの後にUIコントロールにアクセスする必要があります.したがって、ConfigureAwait(false)は を乱用することはできない.
1、WPF/WinFormプログラム
2、asp.Net(asp.net mvcを除く)プログラム
デッドロックの発生原理
非同期メソッドで返されるTaskに対してWait()を呼び出したりResultプロパティにアクセスしたりすると、デッドロックが発生する可能性があります.
次のWPFコードでデッドロックが発生します.
private void Button_Click_7(object sender, RoutedEventArgs e)
{
Method1().Wait();
}
private async Task Method1()
{
await Task.Delay(100);
txtLog.AppendText(" ");
}
次のasp.Netmvcコードにもデッドロックが発生します.
public ActionResult Index()
{
string s=Method1().Result;
return View();
}
private async Task<string> Method1()
{
await Task.Delay(100);
return "hello";
}
WPFコードの例として、イベントプロセッサはMethod 1を呼び出し、Taskオブジェクトを取得し、TaskのWaitメソッドを呼び出し、Taskオブジェクトが「完了」するまで、自分の存在するスレッド、すなわちメインスレッドをブロックする.戻ってきたTaskオブジェクトを「完了」するには、メインスレッド上でawait後のコードを実行する必要があります.メインスレッドはすでにブロックされており、Taskオブジェクトの完了を待っています.するとデッドロックが発生します.
asp.Netmvcコードは同じ理屈です.
デッドロックを回避する方法
次のコードは問題ありません.
private void Button_Click_8(object sender, RoutedEventArgs e)
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://www.baidu.com/");
string html = httpClient.GetStringAsync("/").Result;
txtLog.AppendText(html);
}
次のコードにも問題はありません.
private void Button_Click_8(object sender, RoutedEventArgs e)
{
string html = GetHtml();
txtLog.AppendText(html);
}
private string GetHtml()
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://www.baidu.com/");
return httpClient.GetStringAsync("/").Result;
}
下にはデッドロックが発生します.
private void Button_Click_8(object sender, RoutedEventArgs e)
{
string html = GetHtml().Result;
txtLog.AppendText(html);
}
private async Task<string> GetHtml()
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://www.baidu.com/");
return await httpClient.GetStringAsync("/");
}
なぜHttpClientのGetStringAsync()から返されたTaskでResutにアクセスしてデッドロックは発生せず、自分が書いたコードでデッドロックが発生したのですか?
monoのHttpClientソースコードから、いくつかのヒントを見つけることができます.
すべてのawait式の後に、ConfigureAwait(false)が追加されています.
return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
Taskのmsdnドキュメントから分かるように、ConfigureAwait(false)はawait以降のコードが元のcontext(スレッドと理解できる)上で実行されないことを示す.
これにより,最初のWPFコードを例にとると,メインスレッドがブロックされ,Taskオブジェクトの「完了」を待つ.Method 1が100 ms呼び出された後、別のスレッドでawaitの後のコードが実行され、Taskオブジェクトが完了する.メインスレッドは実行を再開します.
HttpClientを使用してデッドロックを起こすコードを以下の形式に変更します.
private void Button_Click_8(object sender, RoutedEventArgs e)
{
string html = GetHtml().Result;
txtLog.AppendText(html);
}
private async Task<string> GetHtml()
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://www.baidu.com/");
return await httpClient.GetStringAsync("/").ConfigureAwait(false);
}
デッドロックは現れないことがわかります
について述べる