Async方法死锁的问题 Don't Block on Async Code(转)
今天调试requet.GetRequestStreamAsync异步方法出现不返回的问题,可能是死锁了。看到老外一篇文章解释了异步方法死锁的问题,懒的翻译,直接搬过来了。
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
This is a problem that is brought up repeatedly on the forums and Stack Overflow. I think it’s the most-asked question by async newcomers once they’ve learned the basics.
UI Example
Consider the example below. A button click will initiate a REST call and display the results in a text box (this sample is for Windows Forms, but the same principles apply to any UI application).
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
public void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
textBox1.Text = jsonTask.Result;
}
The “GetJson” helper method takes care of making the actual REST call and parsing it as JSON. The button click handler waits for the helper method to complete and then displays its results.
This code will deadlock.
ASP.NET Example
This example is very similar; we have a library method that performs a REST call, only this time it’s used in an ASP.NET context (Web API in this case, but the same principles apply to anyASP.NET application):
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
public class MyController : ApiController
{
public string Get()
{
var jsonTask = GetJsonAsync(...);
return jsonTask.Result.ToString();
}
}
This code will also deadlock. For the same reason.
What Causes the Deadlock
Here’s the situation: remember from my intro post that after you await a Task, when the method continues it will continue in a context.
In the first case, this context is a UI context (which applies to any UI except Console applications). In the second case, this context is an ASP.NET request context.
One other important point: an ASP.NET request context is not tied to a specific thread (like the UI context is), but it does only allow one thread in at a time. This interesting aspect is not officially documented anywhere AFAIK, but it is mentioned in my MSDN article about SynchronizationContext.
So this is what happens, starting with the top-level method (Button1_Click for UI / MyController.Get for ASP.NET):
- The top-level method calls GetJsonAsync (within the UI/ASP.NET context).
- GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
- GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
- GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the GetJsonAsync method later. GetJsonAsync returns an uncompleted Task, indicating that the GetJsonAsync method is not complete.
- The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
- … Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
- The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
- Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for the context to be free so it can complete.
For the UI example, the “context” is the UI context; for the ASP.NET example, the “context” is the ASP.NET request context. This type of deadlock can be caused for either “context”.
Preventing the Deadlock
There are two best practices (both covered in my intro post) that avoid this situation:
- In your “library” async methods, use ConfigureAwait(false) wherever possible.
- Don’t block on Tasks; use async all the way down.
Consider the first best practice. The new “library” method looks like this:
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
This changes the continuation behavior of GetJsonAsync so that it does not resume on the context. Instead, GetJsonAsync will resume on a thread pool thread. This enables GetJsonAsync to complete the Task it returned without having to re-enter the context.
Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. You would have to use ConfigureAwait(false) for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code. Using ConfigureAwait(false) to avoid deadlock is at best just a hack).
As the title of this post points out, the better solution is “Don’t block on async code”.
Consider the second best practice. The new “top-level” methods look like this:
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...);
textBox1.Text = json;
}
public class MyController : ApiController
{
public async Task<string> Get()
{
var json = await GetJsonAsync(...);
return json.ToString();
}
}
This changes the blocking behavior of the top-level methods so that the context is never actually blocked; all “waits” are “asynchronous waits”.
Note: It is best to apply both best practices. Either one will prevent the deadlock, but both must be applied to achieve maximum performance and responsiveness.
Resources
- My introduction to async/await is a good starting point.
- Stephen Toub’s blog post Await, and UI, and deadlocks! Oh, my! covers this exact type of deadlock (in January of 2011, no less!).
- If you prefer videos, Stephen Toub demoed this deadlock live (39:40 - 42:50, but the whole presentation is great!). Lucian Wischik also demoed this deadlock using VB (17:10 - 19:15).
- The Async/Await FAQ goes into detail on exactly when contexts are captured and used for continuations.
This kind of deadlock is always the result of mixing synchronous with asynchronous code. Usually this is because people are just trying out async with one small piece of code and use synchronous code everywhere else. Unfortunately, partially-asynchronous code is much more complex and tricky than just making everything asynchronous.
If you do need to maintain a partially-asynchronous code base, then be sure to check out two more of Stephen Toub’s blog posts: Asynchronous Wrappers for Synchronous Methods and Synchronous Wrappers for Asynchronous Methods, as well as my AsyncEx library.
Async方法死锁的问题 Don't Block on Async Code(转)的更多相关文章
- ASP.NET MVC 如何在一个同步方法(非async)方法中等待async方法
问题 首先,在ASP.NET MVC 环境下对async返回的Task执行Wait()会导致线程死锁.例: public ActionResult Asv2() { //dead lock var t ...
- MVC 如何在一个同步方法(非async)方法中等待async方法
MVC 如何在一个同步方法(非async)方法中等待async方法 问题 首先,在ASP.NET MVC 环境下对async返回的Task执行Wait()会导致线程死锁.例: public Actio ...
- 异步编程系列第04章 编写Async方法
p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...
- 水火难容:同步方法调用async方法引发的ASP.NET应用程序崩溃
之前只知道在同步方法中调用异步(async)方法时,如果用.Result等待调用结果,会造成线程死锁(deadlock).自己也吃过这个苦头,详见等到花儿也谢了的await. 昨天一个偶然的情况,造成 ...
- C# async await 死锁问题总结
可能发生死锁的程序类型 1.WPF/WinForm程序 2.asp.net (不包括asp.net mvc)程序 死锁的产生原理 对异步方法返回的Task调用Wait()或访问Result属性时,可能 ...
- .NET(C#):await返回Task的async方法
众所周知,async方法只可以返回void,Task和Task<T>. 对于返回void的async方法,它并不是awaitable,所以其他方法不能用await方法来调用它,而返回Tas ...
- 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法
参考原贴地址:https://blog.csdn.net/clementad/article/details/47339519 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Trans ...
- 【转】在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法
参考 原文链接 @Transactional does not work on method level 描述 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational) ...
- async方法:async+await
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
随机推荐
- QuickBI助你成为分析师-数据建模(一)
摘要: 数据集编辑功能界面介绍以及常见问题总结. 在数据集编辑界面可以进行数据建模来更好的展示数据,创建数据集默认将数值类型字段作为度量,日期.字符串等类型作为维度,度量可以根据维度分组展示.下面来介 ...
- 安装Stunnel来实现正向代理邮件
文:铁乐与猫 2017年8月 一开始我是使用yum install来安装stunnel的 感觉版本低点也无所谓,毕竟只是拿来加密代理一下邮件收发. 可是后来发现之前下载的最新官网版本的tar包里有很多 ...
- jQuery: 刨根问底 attr and prop两个函数的区别
In this short post I will explain the difference between attributes and properties in HTML. The .pro ...
- 【转】爬虫的一般方法、异步、并发与框架scrapy的效率比较
该文非原创文字,文字转载至 jclian91 链接:https://www.cnblogs.com/jclian91/p/9799697.html Python爬虫的N种姿势 问题的由来 ...
- Css中路径data用法
Data URI scheme是在RFC2397中定义的,目的是将一些小的数据,直接嵌入到网页中,从而不用再从外部文件载入. data:,文本数据 data:text/plain,文本数据 data: ...
- Extjs TreePanel API详解
转自:http://web.qhwins.com/CSS-JS-XML/2011091312092944999107.html config定义{ animate : Boolean, contain ...
- AOP的本质
AOP的本质是HOOK: HOOK的本质是:新函数包含原函数或新函数替换原函数: 需要解决的问题: 1.新函数的生成: 2.新函数的调用机制: 3.原函数的调用机制: 新函数的生成: 1.将已有的动态 ...
- virtualbox+vagrant学习-4-Vagrantfile-2-Configuration Version
Configuration Version 配置版本是vagrant 1.1+能够与vagrant 1.0保持向后兼容的机制.同时引入了引人注目的新特性和配置选项. 如果你运行了vagrant ini ...
- 703. Kth Largest Element in a Stream
题目来源: https://leetcode.com/problems/kth-largest-element-in-a-stream/ 自我感觉难度/真实难度: 题意: 这个题目的意思解读了半天,没 ...
- 垂直方向兼容显示的内容多少的情况样式Flex布局
使用flex弹性布局,无论里面的元素显示几个,都会居中显示,父元素设置成如下样式 display: flex; flex-direction: column; justify-content: cen ...