MoMaek淺陌
涉及 C# 5.0 引入的 async/await,但在控制檯輸出示例時經常會採用 C# 6.0 的 $"" 來拼接字串,相當於string.Format() 方法。
序言
啟動程式之後,系統會在記憶體中建立一個新的程序。程序是構成執行程式資源的集合。在程序內部,有稱為執行緒的核心物件,它代表的是真正的執行程式。系統會在 Main 方法的第一行語句就開始執行緒的執行。
執行緒: ①預設情況,一個程序只包含一個執行緒,從程式的開始到執行結束; ②執行緒可以派生自其它執行緒,所以一個程序可以包含不同狀態的多個執行緒,來執行程式的不同部分; ③一個程序中的多個執行緒,將共享該程序的資源; ④系統為處理器執行所規劃的單元是執行緒,而非程序。
為什麼要使用非同步程式設計
提高程式的效能,相應多端請求或者提高響應速度,採用非同步程式設計。在非同步的程式中,程式碼不需要按照編寫是的順序執行。需要用到5.0引入的 async/await 來構建非同步方法。
不用非同步和使用非同步的比較
未使用非同步執行結果 使用非同步執行結果
技巧類提示: 直接拼接字串是一種比較消耗效能的手段,如果對字串連線有效能要求的話應該使用StringBuilder方法。
程式碼解釋
OldTest.async.await程式碼:
private static readonly Stopwatch stopwatch = new Stopwatch(); static void Main(string[] args) { stopwatch.Start(); const string strTest1 = "https://www.csdn.net/"; const string strTest2 = "https://blog.csdn.net/qq_36981814"; var strCountResult1 = CountChars(1, strTest1); var strCountResult2 = CountChars(2, strTest2); for (int i = 0; i < 3; i++) { ExtraOperation(i + 1); } Debug.WriteLine($"{strTest1} 的字元個數:{strCountResult1}"); Debug.WriteLine($"{strTest2} 的字元個數:{strCountResult2}"); } private static void ExtraOperation(int id) { var s = ""; for (var i = 0; i < 6000; i++) { s += i; } Debug.WriteLine($"id={id}的 ExtraOperation 方法完成:{stopwatch.ElapsedMilliseconds} ms"); } private static int CountChars(int id, string address) { var wc = new WebClient(); Debug.WriteLine($"開始呼叫 id = {id}:{stopwatch.ElapsedMilliseconds} ms"); var result = wc.DownloadString(address); Debug.WriteLine($"呼叫完成 id = {id}:{stopwatch.ElapsedMilliseconds} ms"); return result.Length; }
NewTest.async.await程式碼:
private static readonly Stopwatch stopwatch = new Stopwatch(); static void Main(string[] args) { stopwatch.Start(); const string strTest1 = "https://www.csdn.net/"; const string strTest2 = "https://blog.csdn.net/qq_36981814"; Task<int> strCountResult1 = CountCharsAsync(1, strTest1); Task<int> strCountResult2 = CountCharsAsync(2, strTest2); for (int i = 0; i < 3; i++) { ExtraOperation(i + 1); } Debug.WriteLine($"{strTest1} 的字元個數:{strCountResult1.Result}"); Debug.WriteLine($"{strTest2} 的字元個數:{strCountResult2.Result}"); } private static void ExtraOperation(int id) { var s = ""; for (var i = 0; i < 6000; i++) { s += i; } Debug.WriteLine($"id={id}的 ExtraOperation 方法完成:{stopwatch.ElapsedMilliseconds} ms"); } private static async Task<int> CountCharsAsync(int id, string address) { var wc = new WebClient(); Debug.WriteLine($"開始呼叫 id = {id}:{stopwatch.ElapsedMilliseconds} ms"); var result = await wc.DownloadDataTaskAsync(address); Debug.WriteLine($"呼叫完成 id = {id}:{stopwatch.ElapsedMilliseconds} ms"); return result.Length; }
①從 Main 方法執行到 CountCharactersAsync(1, url1) 方法時,該方法會立即返回,然後才會呼叫它內部的方法開始下載內容。該方法返回的是一個 Task<int> 型別的佔位符物件,表示計劃進行的工作。這個佔位符最終會返回 int 型別的值。 ②這樣就可以不必等 CountCharactersAsync(1, url1) 方法執行完成就可以繼續進行下一步操作。到執行 CountCharactersAsync(2, url2) 方法時,跟 ① 一樣返回 Task<int> 物件。 ③然後,Main 方法繼續執行三次 ExtraOperation 方法,同時兩次 CountCharactersAsync 方法依然在持續工作 。 ④t1.Result 和 t2.Result 是指從 CountCharactersAsync 方法呼叫的 Task<int> 物件取結果,如果還沒有結果的話,將阻塞,直有結果返回為止。
asynic/await結構
同步方法:一個程式呼叫某個方法,等到其執行完成之後才進行下一步操作。這也是預設的形式。 非同步方法:一個程式呼叫某個方法,在處理完成之前就返回該方法。通過 async/await 我們就可以實現這種型別的方法。 async/await 結構可分成三部分: (1)呼叫方法:該方法呼叫非同步方法,然後在非同步方法執行其任務的時候繼續執行; (2)非同步方法:該方法非同步執行工作,然後立刻返回到呼叫方法; (3)await 表示式:用於非同步方法內部,指出需要非同步執行的任務。一個非同步方法可以包含多個 await 表示式(不存在 await 表示式的話 IDE 會發出警告)。
非同步方法詳解
非同步方法:在執行完成前立即返回呼叫方法,在呼叫方法繼續執行的過程中完成任務。 語法分析: (1)關鍵字:方法頭使用 async 修飾。 (2)要求:包含 N(N>0) 個 await 表示式(不存在 await 表示式的話 IDE 會發出警告),表示需要非同步執行的任務。 (3)返回型別:只能返回 3 種類型(void、Task 和 Task<T>)。Task 和 Task<T> 標識返回的物件會在將來完成工作,表示呼叫方法和非同步方法可以繼續執行。 (4)引數:數量不限,但不能使用 out 和 ref 關鍵字。 (5)命名約定:方法字尾名應以 Async 結尾。 (6)其它:匿名方法和 Lambda 表示式也可以作為非同步物件;async 是一個上下文關鍵字;關鍵字 async 必須在返回型別前。
非同步方法簡單結構圖