ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await

原文: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

版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!

原文地址:https://www.cnblogs.com/lonelyxmas/p/12154063.html

时间: 2024-08-24 01:48:09

ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await的相关文章

ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射

原文:ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射 本章主要简单介绍下在ASP.NET Core中如何使用AutoMapper进行实体映射.在正式进入主题之前我们来看下几个概念: 1.数据库持久化对象PO(Persistent Object):顾名思义,这个对象是用来将我们的数据持久化到数据库,一般来说,持久化对象中的字段会与数据库中对应的 table 保持一致. 2.视图对象VO(View Object):视图对象 V

ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)

原文:ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用) 在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入,本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入. PS:本章将主要采用构造函数注入的方式,下一章将继续分享如何使之能够同时支持属性注入的方式. 约定: 1.仓储层接口都以“I”开头,以“Repos

ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入

在正式进入主题之前我们来看下几个概念: 一.依赖倒置 依赖倒置是编程五大原则之一,即: 1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象. 2.抽象不能依赖于具体,具体依赖于抽象. 其中上层就是指使用者,下层就是指被使用者. 二.IoC控制反转 控制反转(IoC,全称Inversion of Control)是一种思想,所谓“控制反转”,就是反转获得依赖对象的过程. 三.依赖注入(DI) 依赖注入设计模式是一种在类及其依赖对象之间实现控制反转(IoC)思想的技术. 所谓依赖注入(DI,全

ASP.NET Core Web 应用程序系列(三)- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)

在上一章中主要和大家分享了在ASP.NET Core中如何使用Autofac替换自带DI进行构造函数的批量依赖注入,本章将和大家继续分享如何使之能够同时支持属性的批量依赖注入. 约定: 1.仓储层接口都以“I”开头,以“Repository”结尾.仓储层实现都以“Repository”结尾. 2.服务层接口都以“I”开头,以“Service”结尾.服务层实现都以“Service”结尾. 接下来我们正式进入主题,在上一章的基础上我们再添加一个web项目TianYa.DotNetShare.Core

Asp.Net Core Web应用程序—探索

前言 作为一个Windows系统下的开发者,我对于Core的使用机会几乎为0,但是考虑到微软的战略规划,我觉得,Core还是有先了解起来的必要. 因为,目前微软已经搞出了两个框架了,一个是Net标准(.NetFramework),一个是Net Core. 而新特性的更新几乎都是在Net Core这个框架中. 所以,考虑到未来,一旦Core完善了,那微软肯定会放弃现在的.NetFrameWork. 因此,.Net程序员集体改用Net Core,想来,一定是大趋势. 所以让我们怀着探索的精神来看看A

ASP.NET Core Web 应用程序开发期间部署到IIS自定义主机域名并附加到进程调试

原文:ASP.NET Core Web 应用程序开发期间部署到IIS自定义主机域名并附加到进程调试 想必大家之前在进行ASP.NET Web 应用程序开发期间都有用到过将我们的网站部署到IIS自定义主机域名并附加到进程进行调试. 那我们的ASP.NET Core Web 应用程序又是如何部署到我们的IIS上面进行调试的呢,接下来我们来简单介绍下: 一.安装IIS所需的Host扩展(Windows Server Hosting) 下载地址:https://dotnet.microsoft.com/

使用docker部署Asp.net core web应用程序--图文教程

要想参考本文做实验,可以参考上一篇文章,关于docker的简单操作,写的比较详细. 拉取aspnetcore最新docker镜像 从阿里云的docker镜像拉取,因为前面我们针对docker镜像做过配置. [[email protected] ~]# docker pull microsoft/aspnetcore 根据你的网速等待拉取成功. [[email protected] ~]# docker images 执行上面的命令,如果能看到aspnetcore镜像,则表示拉取成功. 如果我们想

循序渐进学.Net Core Web Api开发系列【1】:开发环境

系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.本篇概述 本篇不打算描述如何通过Visual Studio创建一个项目之类的话题,主要描述以下内容: 1.使用NuGet和Bower引入第三方库 2.Linux下安装运行环境 3.关于安装虚拟机时碰到的网络设置的问题 实验环境:Windows 10 ,Visual Studio 2017 ,VM 14 , Cent

循序渐进学.Net Core Web Api开发系列【8】:访问数据库(基本功能)

系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇讨论如何连接数据库,包括连接SQL Server 和 连接MySQL,然后做一些基本的数据操作. 二.连接SQL Server 首先通过NuGet添加相关的包: 新建一个实体类: public class Product { [Key] public string Code { get; set; } p