本系列將和大家分享下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
版權聲明:如有雷同純屬巧合,如有侵權請及時聯繫本人修改,謝謝!!!