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.net mvc代码也会出现死锁:
public ActionResult Index() { string s=Method1().Result; return View(); } private async Task<string> Method1() { await Task.Delay(100); return "hello"; }
以WPF代码为例,事件处理器调用Method1,得到Task对象,然后调用Task的Wait方法,阻塞自己所在的线程,即主线程,直到Task对象“完成”。而返回的Task对象要想“完成”,必须在主线程上执行await之后的代码。而主线程早就处于阻塞状态,它在等待Task对象完成!于是死锁就产生了。
asp.net mvc代码是同样的道理。
可以试验一下,下面的代码是不会有问题的:
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对象“完成”;Method1被调用100ms后,在另外的线程上执行了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); }
可以发现,死锁不会出现了