C#基础系列——异步编程初探:async和await
https://www.cnblogs.com/landeanfen/p/4734252.html
之前的那篇 C#基础系列——多线程的常见用法详解 就讲到了多线程new Thread()的方式对于有返回值类型的委托是没有解决方案的,如果需要返回值,必须要依靠异步的方式。了解异步之前,我们先来看看Thread对象的升级版本Task对象:
1、Task对象的前世今生:Task对象是.Net Framework 4.0之后出现的异步编程的一个重要对象。在一定程度上来说,Task对象可以理解Thread对象的一个升级产品。既然是升级产品,那它肯定有他的优势,比如我们上面Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
static void Main(string[] args) { Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var strRes = Task.Run<string>(() => { return GetReturnResult(); });//启动Task执行方法 Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine(strRes.Result);//得到方法的返回值 Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.ReadLine(); } static string GetReturnResult() { Thread.Sleep(2000); return "我是返回值"; }
先来看结果:
从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
static void Main(string[] args) { Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var strRes = Task.Run<string>(() => { return GetReturnResult(); }); Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine(strRes.Result); Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.ReadLine(); } static string GetReturnResult() { Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); return "我是返回值"; }
结果:
由此可以得知,Task.Run<string>(()=>{}).Reslut是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
Task对象的用法如下:
//用法一 Task task1 = new Task(new Action(MyAction)); //用法二 Task task2 = new Task(delegate { MyAction(); }); //用法三 Task task3 = new Task(() => MyAction()); Task task4 = new Task(() => { MyAction(); }); task1.Start(); task2.Start(); task3.Start(); task4.Start();
由上可知,Task对象的构造函数传入的是一个委托,既然能传入Action类型的委托,可想而知Action的16中类型的参数又可以派上用场了。于是乎Task对象参数的传递就不用多说了吧。详见 C#基础系列——委托和设计模式(一)里面Action委托的用法。
2、初识 async & await。
static void Main(string[] args) { Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId); TestAsync(); Console.ReadLine(); } static async Task TestAsync() { Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var name = GetReturnResult(); Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); } static async Task<string> GetReturnResult() { Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId); return await Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId); return "我是返回值"; }); }
结果:
我们来看看程序的执行过程:
由上面的结果可以得到如下结论:
(1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别。
(2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。
(3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。
(4)是否async关键字只能标识返回Task对象的方法呢。我们来试试:
异步方法的返回类型必须为void、Task或者Task<T>类型。也就是说async要么是void,要么和Task关联。
3、除了await关键字,Task对象还有另外一种方式等待执行结果。
static async Task TestAsync() { Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var name = GetReturnResult(); Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name.GetAwaiter().GetResult(), DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); }
这样可以得到相同的结果。
https://www.cnblogs.com/landeanfen/p/4734252.htmlname.GetAwaiter()这个方法得到的是一个TaskAwaiter对象,这个对象表示等待完成的异步任务的对象,并提供结果的参数。所以除了能完成await关键字的等待之外,它还能做一些其他的操作。我们将TaskAwaiter转到定义
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion { public bool IsCompleted { get; } public TResult GetResult(); public void OnCompleted(Action continuation); public void UnsafeOnCompleted(Action continuation); }
IsCompleted:获取一个值,该值指示异步任务是否已完成。
GetResult():得到执行的结果。这个方法和await关键字效果相同。
OnCompleted():传入一个委托,在任务执行完成之后执行。
UnsafeOnCompleted():计划与此 awaiter 相关异步任务的延续操作。
由此可以看出,await关键字实际上就是调用了TaskAwaiter对象的GetResult()方法。
原文地址:https://www.cnblogs.com/nafio/p/11584464.html
- 4927 线段树练习5
- codevs4919 线段树练习4
- 利用OpenCV和深度学习实现人脸检测
- 洛谷P2676 超级书架
- 洛谷P1720 月落乌啼算钱
- 2017.10.1解题报告
- 这个包绝对值得你用心体验一次!
- Python之函数的进阶(带参数的装饰器)
- 2017.10.2解题报告
- MVC 5 Scaffolder + EntityFramework+UnitOfWork Pattern 代码生成工具集成Visual Studio 2013
- 左手用R右手Python系列——百度地图API调用与地址解析/逆解析
- OpenCV实战:人脸关键点检测(FaceMark)
- Asp.Net MVC +EntityFramework主从表新增编辑操作的实现(删除操作怎么实现?)
- 模板模板模板模板模板模板模板模板模板模板模板模板模板模板模板模板模板模板模板模板
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Cypress端到端自动化测试学习笔记
- python常用文本串处理库学习笔记
- python+locust性能测试学习笔记
- 为什么编译原理被称为龙书?
- 用好Jackson,操作Json节省一半时间
- Java|类的继承中两种错误的解决方式 |案例介绍
- 3分钟短文 | PHP时不时蹦出这串神秘字符,有认识的吗?
- 五分钟看懂 Nginx 负载均衡
- 干掉公式 —— numpy 就该这么学
- 全面解析YOLO V4网络结构
- 刺激!一行代码即可导出所有浏览记录
- 你不知道的 node 爬虫原来这么简单
- 带货直播系统源码中,商品详情页是如何搭建起来的
- 基于飞桨复现CVPR 2020 GhostNet的全程解析
- 【即时通信IM】红包消息如何构建?