本文重點介紹的是.NET Framework4.5 推出的異步編程方案 async await
請先看個5分鍾的微軟演示的視頻:
視頻地址: https://channel9.msdn.com/Blogs/ASP-NET-Site-Videos/async-and-await
網絡上已經有很多文章介紹了這個技術點的應用方式,但是舉的例子都是.NET 自帶提供的系統異步方法
所以有些同學就看不大懂,如果是非系統自帶的,如何實現異步。
實際上,有時候一點就能通,把異步的本質了解明白了,也就懂了。
異步編程的本質就是 新開任務線程來處理
這個如何理解呢,請看下文介紹異步編程發展史
static void Main(){ new Thread(Go).Start(); // .NET 1.0開始就有的 Task.Factory.StartNew(Go); // .NET 4.0 引入了 TPL Task.Run(new Action(Go)); // .NET 4.5 新增了一個Run的方法 }
這里大家不難看出,.NET 4.5之后引入了 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<Result>,catch 到的異常會包裝在屬性里面,調用方法就可以從中獲取異常信息,並選擇正確的處理方式。
2、Task
你如果只是想知道執行的狀態,而不需要一個具體的返回結果時,請使用 Task
與void對比呢,是Task可以使用await進行等待新線程執行完畢。而void不需要等待。
3、Task<TResult>
當你添加 async 關鍵字后,需要返回一個將用於后續操作的對象,請使用 Task<TResult>。
主要有兩種方式獲取結果值,一個是使用 Result 屬性,一個是使用 await。他們的區別在於:如果你使用的是 Result,它帶有阻塞性。即在任務完成之前進行訪問讀取它,當前處於活動狀態的線程都會出現阻塞的情形,一直到結果值可用。所以,在絕大多數情況下,除非你有絕對的理由告訴自己,否則都應該使用 await,而不是屬性 Result 來讀取結果值。
下面我們直接看一些代碼就懂了:
/// <summary> /// 添加多條 /// </summary> /// <param name="list"></param> /// <returns></returns> public virtual bool Add(IEnumerable<T> list) { CreateDataBase();//創建數據庫連接 foreach (T t in list) { this.Add(t); } return true; } public virtual async Task<bool> AddAsync(IEnumerable<T> list) { return await Task.Run(() => this.Add(list)); }
正常情況下,我們約定,異步的方法名均以Async結尾。
以下是服務層調用
/// <summary> /// 日志的json格式字符串 包含 title,url,level,descript /// </summary> /// <param name="content"></param> /// <returns></returns> public async Task<bool> AddAsync(string content) { using (var mongoDbContext = new Log.DAL.DbContext()) { if (!string.IsNullOrWhiteSpace(content)) { Log.Model.Record model = Newtonsoft.Json.JsonConvert.DeserializeObject<Log.Model.Record>(content); model.AddTime = DateTime.Now; var result = await mongoDbContext.Record.AddAsync(model); return result; } else { return false; } } }
再接着,進行控制器中的調用
public async Task<ActionResult> Add(string content) { var flag = await new Log.Service.RecordService().AddAsync(content); ; return View(); }
下面的代碼呢,我們演示一下什么時候需要用到result屬性
//不需要,因為用了await public async Task<ActionResult> Detail(int id) { var model = await new DbContext().Record.FirstOrDefaultAsync(m => m.ID == id); return View(model); } //需要 public async Task<ActionResult> Detail(int id) { var model = new DbContext().Record.FirstOrDefaultAsync(m => m.ID == id).Result; return View(model); }
這樣,我們異步編程就講解完了。本文主要是講解異步是實質,學習這個需要異步有一定的學習和了解。
總結:
-
當你添加 async 關鍵字后,需要返回一個將用於后續操作的對象,請使用 Task<TResult>;
-
你如果只是想知道執行的狀態,而不需要知道具體的返回結果時,請使用 Task;
-
如果在觸發后,你懶得管,請使用 void。
- 請盡量優先使用 Task<TResult> 和 Task 作為異步方法的返回類型。
- 用了await,方法必須使用async來修飾。
參考文章:http://www.cnblogs.com/jesse2013/p/async-and-await.html 這里介紹了異步的詳細發展史。
