你眼中的async/await是什么样的?
又到了周末的code review环节,这次code review发现了一个对async/await的理解问题。让我们直奔主题:
var foodsSearch = new FoodSearchService().SearchAsync();
var fruitsSearch = new FruitSearchService().SearchAsync(); var foods = await foodsSearch; foods.ForEach(f => Console.WriteLine("food:{0}", f.Name)); Console.WriteLine("done");
这是一段使用async/await的代码,算得上是async/await的最佳使用实践。问题出在大家对这段代码理解各有不同,让我们来看看如何理解这段代码:

这个理解正确吗?团队的争论焦点在步骤2上,大家错误的认为await关键字就是等待的意思,为了在第三步拿到结果,线程在步骤2处等待直到FoodSearchService返回结果。如果你也是这样理解的,那么你应该看看下面的代码如何理解?
var foodsSearch = new FoodSearchService().SearchAsync();
var fruitsSearch = new FruitSearchService().SearchAsync(); var foods = foodsSearch.Result; foods.ForEach(f => Console.WriteLine("food:{0}", f.Name)); Console.WriteLine("done");
刚才错误的理解正好是这段代码的描述。那么第一段使用async/await的代码如何理解?
使用await标记的方法调用(上面例子中的foodsSearch)不会阻塞主线程,主线程在步骤2不会等待。
为了说明这个结论,我们用下面的代码模拟await(仅仅是模拟其行为,并不能真确执行),我没有研究过await的实现,但是下面的代码跟await具有相同的行为:
var foodsSearch = new FoodSearchService().SearchAsync();
var fruitsSearch = new FruitSearchService().SearchAsync(); var callback = foodsSearch as ICallBackRegister;
callback.Register<List<Food>>(foods =>
{
foods.ForEach(f => Console.WriteLine("food:{0}", f.Name));
Console.WriteLine("done");
}).Await(foodsSearch);
为了方便大家理解,我们写一个简单的ICallBackRegister实现:
public class CallbackRegister:ICallBackRegister
{
Action<object> _action;
public ICallBackRegister Register<T>(Action<T> callback)
{
_action = o => callback((T) o);
return this;
} public void Await(Task task)
{
task.ContinueWith((result)=>_action(result));
}
}
从上面模拟的代码中我们可以得出两个结论:
1、没有任何阻塞主线程的代码。
2、尽量推迟await的调用,能在步骤2使用await就不要在步骤1使用。因为一旦使用了await,后面所有的代码都变成了await所调用对象的回调,无法跟之前的异步代码并行。即便你在步骤1就使用了await,只能说FoodSearchService和FruitSearchService两者不能并行,但是任然不会阻塞主线程——在主线程上永远没有等待这一说。
我们再看张图来解释一下这期间发生的事情:

设想这样的代码放在一个GUI中Button的click事件中,由于await不会阻塞主线程,界面再不会有假死的情况发生。
this.BackColor = Color.Aquamarine;
this.btnAsyncAwait.BackColor = Color.Blue; var operationA = new LongTimeOperationA().GetValueAsync();
var operationB = new LongTimeOperationB().GetValueAsync(); var valueA =await operationA;
Text = valueA;
同样的道理,在web mvc编程中,如果controller和EF中全程使用async/await,此时假设用户有一个请求过来,IIS会从线程池中取出一个线程来响应用户请求,由于主线程没有任何阻塞,所以IIS会很快将线程回收到了线程池中。当EF返回数据并且返回ActionResult时,IIS再次从线程池中拿出一个线程来对用户请求做响应。所以
正是由于async/await不会阻塞主线程,我们才说async/await会提高IIS的响应能力。
另外async/await的使用并不会提升访问数据库的效率,该花多长时间还得花多长时间。
最后我们给出Task.Result版本的click事件,由于调用Task.Result会阻塞主线程,所以你可以看到界面假死的现象。
this.BackColor = Color.Beige;
this.btnTaskResult.BackColor = Color.BlueViolet; var operationA=new LongTimeOperationA().GetValueAsync();
var operationB=new LongTimeOperationB().GetValueAsync(); var valueA = operationA.Result;
Text = valueA;
代码下载:download
你眼中的async/await是什么样的?的更多相关文章
- async & await 的前世今生(Updated)
async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...
- [.NET] 利用 async & await 的异步编程
利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html 目录 异步编程的简介 异 ...
- [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程
怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html ...
- [.NET] 利用 async & await 进行异步 IO 操作
利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html 序 上次,博主 ...
- [C#] 走进异步编程的世界 - 开始接触 async/await
走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...
- ASP.NET 中的 Async/Await 简介
本文转载自MSDN 作者:Stephen Cleary 原文地址:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx 大多数有关 async ...
- C# async/await 使用总结
今天搞这两个关键字搞得有点晕,主要还是没有彻底理解其中的原理. 混淆了一个调用异步方法的概念: 在调用异步方法时,虽然方法返回一个 Task,但是其中的代码已经开始执行.该方法在调用时,即刻执行了一部 ...
- 【转】async & await 的前世今生
async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...
- async & await 的前世今生
async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...
随机推荐
- Centos版Linux 一些常用操作命令
Linux命令收集 1.文件处理命令:ls 功能描述:显示目录文件 命令英文原意:list 命令所在路径:/bin/ls 执行权限:所有用户 语法: ls 选项[-ald] [文件或目录] -a ...
- XAF学习笔记之-多表头设计
空闲之余,看了下全英文的DEV 的XAF帮助文档,一半的英文我认识他,一半的英文他认识我,反正拆开成26个字母我全认识 不那么啰嗦了,先看效果 如何做:分以下几步 第一步:打开这个文件,这个文件就是X ...
- AC算法 及python实现
零 导言 软件安全课上,老师讲了AC算法,写个博客,记一下吧. 那么AC算法是干啥的呢? ——是为了解决多模式匹配问题.换句话说,就是在大字符串S中,看看小字符串s1, s2,...有没有出现. AC ...
- git使用--git命令项目提交问题总结
提交遇到Error "remote ref does not exist"解决办法:git fetch -p MY_REMOTE eg. git fetch -p o ...
- git conifg
1. git config简介 我们知道config是配置的意思,那么git config命令就是对git进行一些配置.而配置一般都是写在配置文件里面,那么git的配置文件在哪里呢?互动一下,先问下大 ...
- C# 动态加载程序集dll (实现接口)
一.程序集(接口程序集):LyhInterface.Dll namespace LyhInterface { public interface ILyhInterface { void Run(); ...
- JS新手易错点
写给自己 字符串换行不能直接换行,需要在行尾加换行符"\" var a = "aa bb" 是不行的 需要改成 var a="aa\ bb"
- javacript实现不被浏览器拦截打开新窗口
情景: 1.用户发送数据到服务器 2.服务器根据用户的数据生成文档 3.服务器把所生成的文档的下载地址提供给用户 4.用户使用的浏览器自动根据下载地址下载文件 实现: 网上搜索查找了下实现方式,就我查 ...
- UVa 11292 Dragon of Loowater
简单贪心 龙头的直径和人的佣金排序,价值小的人和直径小的配 #include<iostream> #include<cstdio> #include<cmath> ...
- 一个豆瓣API的使用——拒绝思维定式
好久没写博客了,最近一直在用豆瓣API爬数据,不知道以前的是什么样,毕竟刚开始用没多久,就用最新的V2版本,以前的不更新了,可以参照https://developers.douban.com/wiki ...