ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await
ps:异步编程的本质就是新开任务线程来处理。
约定:异步的方法名均以async结尾。
实际上呢,异步编程就是通过task.run()来实现的。
了解线程的人都知道,新开一个线程来处理事务这个很常见,但是在以往是没办法接收线程里面返回的值的。所以这时候就该await出场了,await从字面意思不难理解,就是等待的意思。
执行await的方法必须是async修饰的,并且是task的类型。 异步执行后,返回的信息存储在result属性中。但并非主进程就会卡在await行的代码上,执行到await方法之后主线程继续往下执行,无需等待新的线程执行完再继续。只有当需要用到新线程返回的result结果时,此时主进程才会等待新线程执行完并返回内容。也就是说,若无需用到新线程返回的结果,那么主进程不会等待。
async和await呢,返回类型就3种:void、task、task<tresult>。
1、void
如果在触发后,你懒得管,请使用 void。
void返回类型主要用在事件处理程序中,一种称为“fire and forget”(触发并忘记)的活动的方法。除了它之外,我们都应该尽可能是用task,作为我们异步方法的返回值。
返回void,意味着不能await该异步方法,即可能出现线程阻塞,并且也无法获取exception抛出的异常,通常这些异常会导致我们的程序失败,如果你使用的是task和task<tresult>,catch到的异常会包装在属性里面,调用方法就可以从中获取异常信息,并选择正确的处理方式。
2、task
你如果只是想知道执行的状态,而不需要一个具体的返回结果时,请使用task。
与void对比呢,task可以使用await进行等待新线程执行完毕。而void不需要等待。
3、task<tresult>
当你添加async关键字后,需要返回一个将用于后续操作的对象,请使用task<tresult>。
主要有两种方式获取结果值,一个是使用result属性,一个是使用await。他们的区别在于:如果你使用的是result,它带有阻塞性,即在任务完成之前进行访问读取它,当前处于活动状态的线程都会出现阻塞的情形,一直到结果值可用。所以,在绝大多数情况下,除非你有绝对的理由告诉自己,否则都应该使用await,而不是属性result来读取结果值。
接下来我们来看个例子,在上一章的基础上我们添加异步的方法。
首先是仓储层接口:
using system; using system.collections.generic; using system.text; using system.threading.tasks; using tianya.dotnetshare.model; namespace tianya.dotnetshare.repository { /// <summary> /// 学生类仓储层接口 /// </summary> public interface istudentrepository { /// <summary> /// 根据学号获取学生信息 /// </summary> /// <param name="stuno">学号</param> /// <returns>学生信息</returns> student getstuinfo(string stuno); /// <summary> /// 根据学号获取学生信息(异步) /// </summary> /// <param name="stuno">学号</param> /// <returns>学生信息</returns> task<student> getstuinfoasync(string stuno); } }
接着是仓储层实现:
using system; using system.collections.generic; using system.text; using system.threading.tasks; using tianya.dotnetshare.model; namespace tianya.dotnetshare.repository.impl { /// <summary> /// 学生类仓储层 /// </summary> public class studentrepository : istudentrepository { /// <summary> /// 根据学号获取学生信息 /// </summary> /// <param name="stuno">学号</param> /// <returns>学生信息</returns> public student getstuinfo(string stuno) { //数据访问逻辑,此处为了演示就简单些 var student = new student(); switch (stuno) { case "10000": student = new student() { stuno = "10000", name = "张三", sex = "男", age = 20 }; break; case "10001": student = new student() { stuno = "10001", name = "钱七七", sex = "女", age = 18 }; break; case "10002": student = new student() { stuno = "10002", name = "李四", sex = "男", age = 21 }; break; default: student = new student() { stuno = "10003", name = "王五", sex = "男", age = 25 }; break; } return student; } /// <summary> /// 根据学号获取学生信息(异步) /// </summary> /// <param name="stuno">学号</param> /// <returns>学生信息</returns> public virtual async task<student> getstuinfoasync(string stuno) { return await task.run(() => this.getstuinfo(stuno)); } } }
然后是服务层接口:
using system; using system.collections.generic; using system.text; using system.threading.tasks; using tianya.dotnetshare.model; namespace tianya.dotnetshare.service { /// <summary> /// 学生类服务层接口 /// </summary> public interface istudentservice { /// <summary> /// 根据学号获取学生信息 /// </summary> /// <param name="stuno">学号</param> /// <returns>学生信息</returns> student getstuinfo(string stuno); /// <summary> /// 根据学号获取学生信息(异步) /// </summary> /// <param name="stuno">学号</param> /// <returns>学生信息</returns> task<student> getstuinfoasync(string stuno); } }
再接着是服务层实现:
using system; using system.collections.generic; using system.text; using system.threading.tasks; using tianya.dotnetshare.model; using tianya.dotnetshare.repository; namespace tianya.dotnetshare.service.impl { /// <summary> /// 学生类服务层 /// </summary> public class studentservice : istudentservice { /// <summary> /// 定义仓储层学生抽象类对象 /// </summary> protected istudentrepository sturepository; /// <summary> /// 空构造函数 /// </summary> public studentservice() { } /// <summary> /// 构造函数 /// </summary> /// <param name="sturepository">仓储层学生抽象类对象</param> public studentservice(istudentrepository sturepository) { this.sturepository = sturepository; } /// <summary> /// 根据学号获取学生信息 /// </summary> /// <param name="stuno">学号</param> /// <returns>学生信息</returns> public student getstuinfo(string stuno) { var stu = sturepository.getstuinfo(stuno); return stu; } /// <summary> /// 根据学号获取学生信息(异步) /// </summary> /// <param name="stuno">学号</param> /// <returns>学生信息</returns> public virtual async task<student> getstuinfoasync(string stuno) { return await sturepository.getstuinfoasync(stuno); } } }
最后进行控制器中的调用:
using system; using system.collections.generic; using system.diagnostics; using system.linq; using system.threading.tasks; using microsoft.aspnetcore.mvc; using tianya.dotnetshare.coreautofacdemo.models; using tianya.dotnetshare.service; using tianya.dotnetshare.repository; using tianya.dotnetshare.repository.impl; namespace tianya.dotnetshare.coreautofacdemo.controllers { public class homecontroller : controller { /// <summary> /// 定义仓储层学生抽象类对象 /// </summary> private istudentrepository _sturepository; /// <summary> /// 定义服务层学生抽象类对象 /// </summary> private istudentservice _stuservice; /// <summary> /// 定义服务层学生抽象类对象 /// 属性注入:访问修饰符必须为public,否则会注入失败。 /// </summary> public istudentservice stuservice { get; set; } /// <summary> /// 定义仓储层学生实现类对象 /// 属性注入:访问修饰符必须为public,否则会注入失败。 /// </summary> public studentrepository sturepository { get; set; } /// <summary> /// 通过构造函数进行注入 /// 注意:参数是抽象类,而非实现类,因为已经在startup.cs中将实现类映射给了抽象类 /// </summary> /// <param name="sturepository">仓储层学生抽象类对象</param> /// <param name="stuservice">服务层学生抽象类对象</param> public homecontroller(istudentrepository sturepository, istudentservice stuservice) { this._sturepository = sturepository; this._stuservice = stuservice; } public iactionresult index() { var stu1 = sturepository.getstuinfo("10000"); var stu2 = stuservice.getstuinfo("10001"); var stu3 = _stuservice.getstuinfo("10002"); var stu4 = _sturepository.getstuinfo("1003"); string msg = $"学号:10000,姓名:{stu1.name},性别:{stu1.sex},年龄:{stu1.age}<br />"; msg += $"学号:10001,姓名:{stu2.name},性别:{stu2.sex},年龄:{stu2.age}<br/>"; msg += $"学号:10002,姓名:{stu3.name},性别:{stu3.sex},年龄:{stu3.age}<br/>"; msg += $"学号:10003,姓名:{stu4.name},性别:{stu4.sex},年龄:{stu4.age}<br/>"; return content(msg, "text/html", system.text.encoding.utf8); } public async task<iactionresult> privacy() { var stu = await _stuservice.getstuinfoasync("10000"); return json(stu); } [responsecache(duration = 0, location = responsecachelocation.none, nostore = true)] public iactionresult error() { return view(new errorviewmodel { requestid = activity.current?.id ?? httpcontext.traceidentifier }); } } }
至此完成处理,我们来访问一下/home/privacy,看看是否正常
可以看出是正常的
下面我们演示一下什么时候需要用到result属性:
//用了await则不需要result属性 public async task<iactionresult> privacy() { var stu = await _stuservice.getstuinfoasync("10000"); return json(stu); }
//没有用await则需要result属性 public async task<iactionresult> privacy() { var stu = _stuservice.getstuinfoasync("10000").result; return json(stu); }
至此我们的异步编程就讲解完了。
总结:
1、尽量优先使用task<tresult>和task作为异步方法的返回类型。
2、如果用了await则方法必须使用async来修饰,并且是task的类型。
demo源码:
链接:https://pan.baidu.com/s/1wb0mebm-nh9yfoaynlwo-g 提取码:1ayv
参考博文:https://www.cnblogs.com/fei686868/p/9637310.html
版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!