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(1, 2);
- // 异步方法, 没有 Await
- AddNoAwaitSyncHas(1, 2);
- // 异步方法, 有 Await
- AddHasAwaitAsync(1, 2);
- 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(1000);
- 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(1000);
- 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(5000);
- 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) 之 二 延续操作
第三篇: 聊聊多线程那一些事儿 (task) 之 三 异步取消和异步方法
第四篇: 聊聊多线程那一些事儿 之 四 经典应用(取与舍, 动态创建)
END
为了更高的交流, 欢迎大家关注我的公众号, 扫描下面二维码即可关注, 谢谢:
posted on 2020-01-02 00:30 程序员修炼之旅 阅读(...) 评论(...) 编辑 收藏
来源: https://www.cnblogs.com/xiaoXuZhi/p/XYH_tsak_async_await.html