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 any ASP.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):

  1. The top-level method calls GetJsonAsync (within the UI/ASP.NET context).
  2. GetJsonAsync starts the REST request by calling HttpClient.GetStringAsync (still within the context).
  3. GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
  4. 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.
  5. The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
  6. … Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
  7. 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.
  8. 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:

  1. In your “library” async methods, use ConfigureAwait(false) wherever possible.
  2. Don’t block on Tasks; use async all the way down.

Consider the first best practice.   //UI的修改

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.

Consider the second best practice.     //ASP.NET的修改

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

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.

Answered Questions

There are scores of answered questions out there that are all caused by the same deadlock problem. It has shown up on WinRT, WPF, Windows Forms, Windows Phone, MonoDroid, Monogame, and ASP.NET.

Update (2014-12-01): For more details, see my MSDN article on asynchronous best practices or Section 1.2 in myConcurrency Cookbook.

还有一个姊妹篇http://blog.stephencleary.com/2012/12/dont-block-in-asynchronous-code.html

Don't Block on Async Code的更多相关文章

  1. Async方法死锁的问题 Don't Block on Async Code(转)

    今天调试requet.GetRequestStreamAsync异步方法出现不返回的问题,可能是死锁了.看到老外一篇文章解释了异步方法死锁的问题,懒的翻译,直接搬过来了. http://blog.st ...

  2. Don't Block on Async Code【转】

    http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html This is a problem that is brough ...

  3. Practical Node.js (2018版) 14章, async code in Node

    Asynchronous Code in Node 历史上,Node开发者只能用回调和事件emitters. 现在可以使用一些异步的语法: async module Promises Async/aw ...

  4. Async All the Way

    Asynchronous code reminds me of the story of a fellow who mentioned that the world was suspended in ...

  5. ASP.NET sync over async(异步中同步,什么鬼?)

    async/await 是我们在 ASP.NET 应用程序中,写异步代码最常用的两个关键字,使用它俩,我们不需要考虑太多背后的东西,比如异步的原理等等,如果你的 ASP.NET 应用程序是异步到底的, ...

  6. Async/Await - Best Practices in Asynchronous Programming

    https://msdn.microsoft.com/en-us/magazine/jj991977.aspx Figure 1 Summary of Asynchronous Programming ...

  7. 小心C# 5.0 中的await and async模式造成的死锁

    平时在使用C# 5.0中的await and async关键字的时候总是没注意,直到今天在调试一个ASP.NET项目时,发现在调用一个声明为async的方法后,程序老是莫名其妙的被卡住,就算声明为as ...

  8. Async/Await - Best Practices in Asynchronous Programming z

    These days there’s a wealth of information about the new async and await support in the Microsoft .N ...

  9. async/await 的一些知识

    博文 Don't Block on Async Code What is the purpose of "return await" in C#? Any difference b ...

随机推荐

  1. springBoot jar/war打包部署问题

    先给pom.xml配置导出插件 <!--配置插件将Maven 插件 导出成为jar --> <plugin> <groupId>org.springframewor ...

  2. 三维重建:SLAM算法的考题总结

    参考英文维基:https://en.wikipedia.org/wiki/Slam 参考文档:视觉slam研究分析的一点认识 1. 请简单描述您对机器人的SLAM的概念理解? 答: 机器人需要在自身位 ...

  3. SLAM: 关于Orb_SLAM的使用小综述

    0.ORB_SLAM的官方网站:http://webdiis.unizar.es/~raulmur/orbslam/ 1. 参考知乎上对 orb-slam 的评价:orb-slam在众多SLAM方法中 ...

  4. REST ful

    前后端分离.面向资源.无状态: 请求包含全部信息. 什么是 REST? 下面六条准则定义了一个 REST 系统的特征: 客户-服务器(Client-Server),提供服务的服务器和使用服务的客户需要 ...

  5. Linux 僵尸进程如何处理

    Linux 允许进程查询内核以获得其父进程的 PID,或者其任何子进程的执行状态.例如,进程可以创建一个子进程来执行特定的任务,然后调用诸如 wait() 这样的一些库函数检查子进程是否终止.如果子进 ...

  6. [jzoj 5782]【NOIP提高A组模拟2018.8.8】 城市猎人 (并查集按秩合并+复杂度分析)

    传送门 Description 有n个城市,标号为1到n,修建道路花费m天,第i天时,若gcd(a,b)=m-i+1,则标号为a的城市和标号为b的城市会建好一条直接相连的道路,有多次询问,每次询问某两 ...

  7. js中二维数组的创建方法 2017-04-04 14:50 120人阅读 评论(0) 收藏

    法一:var myarr=[[0,1,2],[1,2,3]]; 将[0,1,2]看做原来的0,将[1,2,3]看做原来的1,而二者又分别为子数组 如myarr[0][1]=1,myarr[1][1]= ...

  8. 59.bouncing results

        一.bouncing results成因及解决方案 bouncing results问题,两个document排序,field值相同:不同的shard上,可能排序不同:每次请求轮询路由到不同的 ...

  9. C++判断质数

    using namespace std; bool isPrimeNum(int n) { if(n<2) return true; for(int i=2;i*i<=n;i++) { i ...

  10. Bootstrap 表单控件状态(验证状态)

    在制作表单时,不免要做表单验证.同样也需要提供验证状态样式,在Bootstrap框架中同样提供这几种效果.1..has-warning:警告状态(黄色)2..has-error:错误状态(红色)3.. ...