码迷,mamicode.com
首页 > 其他好文 > 详细

莫阻塞async代码

时间:2016-05-29 13:32:06      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:

最近在深入研究异步模式和async, await关键字的时候看到了Stephen Cleary的这篇文章感觉又提高了一下对这两个keyword的了解,原文链接如下

http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

 

UI Example

考虑下面示例单击按钮启动其他呼叫显示文本框 (此示例 Windows 窗体同样原则适用任何 UI 应用程序) 结果

// 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;
}
"GetJsonAsync"方法进行实际的 REST 调用并解析 JSON按钮单击方法等待帮助器方法结束然后显示结果代码死锁

 

ASP.NET Example

和上面非常相似的例子,执行 REST 调用方法 ASP.NET 上下文中使用 (示例是Web API 适用任何 ASP.NET 应用程序) 

// 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();
  }
}

同样原因,这段代码也会造成死锁。

 

为什么会死锁?

 

第一个要点:一个等待另一个任务完成的方法要继续执行时是在一个上下文中执行。第一种情况下情况下一个 UI 上下文 适用任何 UI 除了控制台应用程序)。第二种情况下 ASP.NET 请求上下文

一个要点  ASP.NET 请求上下文依赖特定线程 用户界面是),允许一个线程进入。

 

下面是会发生的事情,我们从顶层方法 (Button1_Click UI / MyController.Get  ASP.NET)开始说:

  • 顶层方法调用 GetJsonAsync (在UI/ASP.NET 上下文内)。
  • GetJsonAsync 调用 HttpClient.GetStringAsync来进行REST请求 
  • GetStringAsync 返回未完成的Task,表示请求完成
  • GetJsonAsync 在GetStringAsync 返回的Task上进行await。这时上下文状态被捕获,来稍后继续运行 GetJsonAsync 方法GetJsonAsync 返回未完成的Task表明 GetJsonAsync 没有结束
  • 顶层方法阻塞 GetJsonAsync 返回的任务。也就是阻塞了上下文线程
  • …过了一会,REST请求完成GetStringAsync任务完成
  •  GetJsonAsync 现在可以继续运行了它等待它的上下文
  • 死锁顶级方法阻止上下文线程等待 GetJsonAsync 完成 GetJsonAsync 等待上下文被释放

在UI 示例中"上下文"用户界面上下文; 在ASP.NET 示例中"上下文" ASP.NET 请求上下文

 

怎样避免死锁

两个避免情况最佳做法。
 
  1. 异步方法的代码库尽可能使用 ConfigureAwait(false)
  2. 不要阻塞任务; 从头到尾使用异步
考虑第一种。库方法应该这样写:
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);
  }
}
更改 GetJsonAsync 的继续执行的方法行为,它将在线程线程上恢复。从而继续完成任务而不必等待上下文返回

 

第二种方法。"顶层"方法应该这样写:
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();
  }
}
 
更改顶层方法阻塞行为上下文从来没有真正阻止;所有"等待""异步等待"。
注意  这两种方法任何一个都可以避免死锁,但应该同时使用来获得最快的响应速度和系统性能

  

 

 

莫阻塞async代码

标签:

原文地址:http://www.cnblogs.com/odyssey/p/5539228.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!