聊聊多线程那一些事儿 之 五 async.await深度剖析
hello task,咱们又见面啦!!是不是觉得很熟读的开场白,哈哈你哟这感觉那就对了,说明你已经阅读过了我总结的前面4篇关于task的文章,谢谢支持!感觉不熟悉的也没有关系,在文章末尾我会列出前四篇文章的地址,可以点击详细阅读。
前几篇文章分享了以后,无论是公众号还是博客园,都有小伙伴问我async/await的专栏总结分享,既然这样,那今天我们就专门来聊聊关于async/await的那一些事,通过该文章你也该对async的使用还有更加清晰的理解,谢谢!
async/await入门:
async也就是我们说的异步方法,不废话,也不先说那么多的大理论,先上一个简单的实例,通过这个简单的实例实现和asyncd 初相识!!
- static void Main(string[] args)
- {
- Console.WriteLine($"主线程开始,线程ID:{Thread.CurrentThread.ManagedThreadId}\n");
- // 同步实现
- AddSync(, );
- // 异步方法,没有 Await
- AddNoAwaitSyncHas(, );
- // 异步方法,有 Await
- AddHasAwaitAsync(, );
- Console.WriteLine($"主线程结束,线程ID:{Thread.CurrentThread.ManagedThreadId}\n");
- Console.ReadLine();
- return;
- }
- /// <summary>
- /// 同步计算两个数字之和
- /// </summary>
- /// <param name="num1">参数1</param>
- /// <param name="num2">参数2</param>
- /// <returns></returns>
- private static int AddSync(int num1, int num2)
- {
- Thread.Sleep();
- Console.WriteLine($"同步方法,线程ID:{Thread.CurrentThread.ManagedThreadId}\n");
- return num1 + num2;
- }
- /// <summary>
- /// 对两个数字求和 (异步方法,没有 Await)
- /// </summary>
- /// <param name="num1">参数1</param>
- /// <param name="num2">参数2</param>
- /// <returns>结果</returns>
- private static async Task<int> AddNoAwaitSyncHas(int num1, int num2)
- {
- Console.WriteLine($"异步线程没有await前,线程ID:{Thread.CurrentThread.ManagedThreadId}\n");
- // 两个数字求和,假设其中会涉及到很耗时的逻辑
- Thread.Sleep();
- Console.WriteLine($"异步线程没有await后,线程ID:{Thread.CurrentThread.ManagedThreadId}\n");
- return num1 + num2;
- }
- /// <summary>
- /// 对两个数字求和 (异步方法,有 Await)
- /// </summary>
- /// <param name="num1">参数1</param>
- /// <param name="num2">参数2</param>
- /// <returns>结果</returns>
- private static async Task<int> AddHasAwaitAsync(int num1, int num2)
- {
- Console.WriteLine($"异步线程await前,线程ID:{Thread.CurrentThread.ManagedThreadId}\n");
- // 两个数字求和,假设其中会涉及到很耗时的逻辑
- var add = Add(num1, num2);
- int result = await add;
- Console.WriteLine($"异步线程await后,线程ID:{Thread.CurrentThread.ManagedThreadId}\n");
- return result;
- }
- /// <summary>
- /// Task 对两个数字求和
- /// </summary>
- /// <param name="num1">参数1</param>
- /// <param name="num2">参数2</param>
- /// <returns>结果</returns>
- private static Task<int> Add(int num1, int num2)
- {
- // 假设该逻辑执行起来很耗时
- var task = Task.Run(() =>
- {
- Console.WriteLine($"我是Task内部执行开始:线程ID :{Thread.CurrentThread.ManagedThreadId}\n");
- Thread.Sleep();
- Console.WriteLine($"我是Task内部执行结束:线程ID :{Thread.CurrentThread.ManagedThreadId}\n");
- return num1 + num2;
- });
- return task;
- }
执行结果:
结合代码和执行结果,我们分析可以得出以下一些结论:
1、通过async的写法和同步方法在实现和调用上都很相似
2、异步方法async如果没有await关键词,其整体执行都是在主线中运行
----同步调用
3、异步方法async有await关键词,其线程执行分水岭就在await
----await前,async执行还是在主线中执行
----await后,async的执行逻辑会新开一个线程
----也就是说,async其真正的异步还是await实现
----而await修饰的实际是一个task修饰的变量或者返回的类型为task的方法体
----所以最后的最后,async的异步还是通过task来实现的
4、await是不能单独使用,一定是在是和async成对使用
----当然aysnc修饰的方法可以没有await关键词
通过上面的一个简单实例,是不是发现要实现一个异步方法,是不是so easy,是的 ,你没说错,就是那么简单,但是也许你会问,干嘛实现一个异步方法整的的如此复杂,创建了这么多方法,是的,不急不急,我这样写,是为了更加清晰的明白其执行流程。好了,下面我们在一起来探讨一下aysnc/await的组成结构吧!
aysnc/await的组成结构:
其实异步方法的整体结构和一个普通的方法没有多大区别,唯一不一样的点,就是多了一个task逻辑主体,下面简单的分别来概要说明一下每一个环节:
上面的图简单的绘制了一个异步方法在整体执行时的一个执行顺序。
异步方法调用
个人觉得这个没有什么说的,其实很普通方法调用一样,只是说异步方法的调用结果一般为一个Task对象,那么需要获获取其执行结果的值,或者对执行结果需要做一些逻辑处理,这个和操作一个普通的task一样,这儿就不在细说,不清楚的可以看我前面分享的几篇文章,会有详细的说明,谢谢!
aysnc的方法体
通过实例我们应该已经知道,其实异步方法,也就是在普通的方法体上,加了一个async修饰罢了,其简单的结构大概是
private aysnc task MyAysnc(){具体方法实现}
说说aysnc的返回类型
其返回类型有三种情况,每一种情况适用于不同的业务场景,如下:
A、Tsak:其主要适用场景是,主程序只关心异步方法执行状态,不需要和主线程有任何执行结果数据交互。
B、Task<T>:其主要适用场景是,主程序不仅仅关心异步方法执行状态,并且还希望执行后返回一个数据类型为T的结果
C、void: 主程序既不关系异步方法执行状态,也不关心其执行结果,只是主程序调用一次异步方法,对于除事件处理程序以外的代码,通常不鼓励使用 async void 方法,因为调用方不能
task逻辑主体
aysnc为了实现异步,其中最关键的一个点就是await修饰符,await修饰的也就是task实现逻辑主体。task实现逻辑主体,其实在上就是一个task实例,所以其里面的实例逻辑使用和一个普通的task实例定义操作都是一样的,在此也就不在详细说明,前面的几篇文章也有详细的说明了,如果不清楚的可以查看以前的几篇文章。
aysnc/await的原理分析:
在说这一块之前,我们先把写的代码编译后,在通过反编译后发现在代码里面根本找不到aysnc/await关键词,有兴趣的小伙伴,你也可以这样操作分析一下。那么我们就明白了aysnc/await其实是编译器层面给的一个语法糖,是为了方便实现一个异步方罢了。
从反编译后的代码看出编译器新生成一个继承IAsyncStateMachine 的状态机结构asyncd(代码中叫<AddHasAwaitAsync>d__2),下面是基于反编译后的代码来分析的。
IAsyncStateMachine最基本的状态机接口定义:
- public interface IAsyncStateMachine {
- void MoveNext();
- void SetStateMachine(IAsyncStateMachine stateMachine);
- }
好了,说道这儿我们已经知道aysnc/await是编程器层面的一个语法糖,那么我们在来分析一下其执行的流程如下:
第一步:主线程调用 AddHasAwaitAsync(1,2)异步方法
第二步:AddHasAwaitAsync()方法内初始化状态机状态为-1,启动<AddHasAwaitAsync>d__2
第三步:MoveNext方法内部开始执行,task.run实现了把业务逻辑执行丢到线程池中,返回一个可等待的任务句柄。其底层还是借助委托实现。
第四步:到此程序以及开启了两个线程,一个主线程,一个task线程,两个线程相互独立互不阻塞,各自执行对应的业务逻辑。
好了,时间不早了,就先到这儿吧,感觉这一篇文章总结的不怎么好,先这样,后续我们在持续交流,谢谢!
猜您喜欢:
第一篇:聊聊多线程哪一些事儿(task)之 一创建运行与阻塞
第三篇:聊聊多线程那一些事儿(task)之 三 异步取消和异步方法
第四篇:聊聊多线程那一些事儿 之 四 经典应用(取与舍、动态创建)
END
为了更高的交流,欢迎大家关注我的公众号,扫描下面二维码即可关注,谢谢:
聊聊多线程那一些事儿 之 五 async.await深度剖析的更多相关文章
- 聊聊多线程那一些事儿(task)之 三 异步取消和异步方法
hello,咋们又见面啦,通过前面两篇文章的介绍,对task的创建.运行.阻塞.同步.延续操作等都有了很好的认识和使用,结合实际的场景介绍,这样一来在实际的工作中也能够解决很大一部分的关于多线程的业务 ...
- 聊聊多线程哪一些事儿(task)之 三 异步取消和异步方法
hello,咋们又见面啦,通过前面两篇文章的介绍,对task的创建.运行.阻塞.同步.延续操作等都有了很好的认识和使用,结合实际的场景介绍,这样一来在实际的工作中也能够解决很大一部分的关于多线程的业务 ...
- 聊聊多线程哪一些事儿(task)之 一
多线程,一个多么熟悉的词汇,作为一名程序员,我相信无论是从事什么开发语言,都能够轻轻松松说出几种实现多线程的方式,并且在实际工作种也一定用到过多线程,比如:定时器.异步作业等等,如果你说你没有用过多线 ...
- 聊聊多线程哪一些事儿(task)之 二 延续操作
hello,又见面啦,昨天我们简单的介绍了如何去创建和运行一个task.如何实现task的同步执行.如何阻塞等待task集合的执行完毕等待,昨天讲的是task的最基本的知识点,如果你没有看昨天的博客, ...
- async/await 深度理解使用
在vue中使用 eg async created () { await setTimeout(()=>{ console.log(1) },5000); }, async mounted () ...
- 六十五 async/await
用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异 ...
- 温故知新,CSharp遇见异步编程(Async/Await),聊聊异步编程最佳做法
什么是异步编程(Async/Await) Async/Await本质上是通过编译器实现的语法糖,它让我们能够轻松的写出简洁.易懂.易维护的异步代码. Async/Await是C# 5引入的关键字,用以 ...
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿![转载]
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
随机推荐
- Django框架登录验证及产生随机验证码的实例
1:views视图代码 # 登录验证 def login(request): # 使用ajax请求可以使用判断 # if request.is_ajax(): if request.method == ...
- 修改eclipse默认注释
windows-->preference-->Java-->Code Style-->Code Templates -->Comments :注释--> ... 关 ...
- iOS runtime整理
iOS利用Runtime自定义控制器POP手势动画 http://www.cocoachina.com/ios/20150401/11459.html Objective C运行时(runtime) ...
- qt中窗口绘制——图片的绘制
在qt 中,QPixmap 用于表示一张图片,支持png,jpg格式的加载. QPixmap pm("c:/test.png"); 或者 QPixmap pm; pm.load(& ...
- Flask学习之二 模板
继续学习flask 本部分Miguel Grinberg教程的翻译地址:http://www.pythondoc.com/flask-mega-tutorial/templates.html 英文原文 ...
- Libev源码分析01:Libev中的监视器结构(C结构体实现继承)
在Libev的源码中,用到了一种用C实现类似C++中继承的技巧,主要是用宏和结构体实现. 在Libev中,最关键的数据结构就是各种监视器,比如IO监视器,信号监视器等等.这些监视器的多数成员都是一样的 ...
- CSDN-Java培训 - 看看这次会有多少人跟风...
2019年5月8日,闲来无事(最近答辩还没事......),存个档. 看看这一波风口,记录互联网+教育.
- 给radio添加点击事件
1.单独给每个radio添加点击事件 <fieldset id="form-gra-time"> <legend>请选择日期粒度:</legend&g ...
- js获取盒子scrollTop
前言:如何单纯获取某个盒子的滚动值-->> (属性可写可读) var scroll = document.getElementById('box').scrollTop;//获取盒子的滚 ...
- [转载] linux下tar命令解压到指定的目录
参考 http://blog.sina.com.cn/s/blog_62449fcf0100nfar.html linux下tar命令解压到指定的目录 : #tar zxvf /bbs.tar.z ...