1.什么是異步
程序啟動時,系統會在內存中創建一個新的進程,進程是構成運行程序的資源合集,
在進程內部,系統創建了一個稱為線程的內核對象,它代表了真正執行的程序,
關於線程:
默認情況下,一個進程只包含一個線程,從程序開始一直執行到結束,
線程可以派生其他線程,因此在任意時刻,一個進程都可能包含不同狀態的多個線程,來執行程序的不同部分,
系統為處理器執行所規划的單元是線程,不是進程,
不使用異步示例:
class MyDownload { Stopwatch sw = new Stopwatch(); public void DoRun() { const int LargeNumber = 6000000; sw.Start(); int t1 = CountCharacters(1, "http://www.miscrosoft.com"); int t2 = CountCharacters(2, "http://www.illustratedcsharp.com"); CountToALargeNumber(1, LargeNumber); CountToALargeNumber(2, LargeNumber); CountToALargeNumber(3, LargeNumber); CountToALargeNumber(4, LargeNumber); Console.WriteLine("Chars in http://www.miscrosoft.com {0}", t1); Console.WriteLine("Chars in http://www.illustratedcsharp.com {0}", t2); } private int CountCharacters(int id, string uristring) { WebClient wc1 = new WebClient(); Console.WriteLine("Starting call {0} : {1, 4:N0} ms", id, sw.Elapsed.TotalMilliseconds); string result = wc1.DownloadString(new Uri(uristring)); Console.WriteLine(" Call {0} completed : {1, 4:N0} ms", id, sw.Elapsed.TotalMilliseconds); return result.Length; } public void CountToALargeNumber(int id, int value) { for (long i = 0; i < value; i++) ; Console.WriteLine(" End counting {0} : {1, 4:N0} ms", id, sw.Elapsed.TotalMilliseconds); } }
class Program { static void Main(string[] args) { MyDownload md = new MyDownload(); md.DoRun(); Console.ReadKey(); } }
如果我們能初始化兩個CountCharacter調用,無需等待結果,而是直接執行4個CountToALargeNumber調用,然后在兩個CountCharacter方法調用結束后在獲取結果,就可以顯著的提升性能,即async/await,
class MyDownload { Stopwatch sw = new Stopwatch(); public void DoRun() { const int LargeNumber = 6000000; sw.Start(); Task<int> t1 = CountCharactersAsync(1, "http://www.miscrosoft.com"); Task<int> t2 = CountCharactersAsync(2, "http://www.illustratedcsharp.com"); CountToALargeNumber(1, LargeNumber); CountToALargeNumber(2, LargeNumber); CountToALargeNumber(3, LargeNumber); CountToALargeNumber(4, LargeNumber); Console.WriteLine("Chars in http://www.miscrosoft.com {0}", t1.Result); Console.WriteLine("Chars in http://www.illustratedcsharp.com {0}", t2.Result); } private async Task<int> CountCharactersAsync(int id, string uristring) { WebClient wc1 = new WebClient(); Console.WriteLine("Starting call {0} : {1, 4:N0} ms", id, sw.Elapsed.TotalMilliseconds); string result = await wc1.DownloadStringTaskAsync(new Uri(uristring)); Console.WriteLine(" Call {0} completed : {1, 4:N0} ms", id, sw.Elapsed.TotalMilliseconds); return result.Length; } public void CountToALargeNumber(int id, int value) { for (long i = 0; i < value; i++) ; Console.WriteLine(" End counting {0} : {1, 4:N0} ms", id, sw.Elapsed.TotalMilliseconds); } }
class Program { static void Main(string[] args) { MyDownload md = new MyDownload(); md.DoRun(); Console.ReadKey(); } }
這些線程都是在主線程中完成的,我們沒有創建任何額外的線程,
2.asyncawait結構
如果一個程序調用某個方法,等待其執行所有處理后才繼續執行,這樣的方法為同步方法,
異步方法為處理完成之前就返回到調用方法,
c#的async/await特性可以創建並使用異步方法,該特性由三個部分組成,
調用方法:該方法調用異步方法,然后在異步方法執行其任務的時候繼續執行,
異步方法:該方法執行異步工作,然后立即返回到調用方法,
await表達式:用於異步方法內部,指明需要異步執行的任務,一個異步方法可以包含任意多個await表達式,如果一個不包含編譯器會發出警告,
調用方法如下:
class Program { static void Main(string[] args) { Task<int> value = DoAsyncStuff.CalculateSumAsync(5, 6); // 調用方法 Console.WriteLine("value:{0}", value.Result); Console.ReadKey(); } }
異步方法如下:
static class DoAsyncStuff { public static async Task<int> CalculateSumAsync(int n1, int n2) { int sum = await Task.Run(() => GetSum(n1, n2)); // 異步方法 await表達式 return sum; } private static int GetSum(int n1, int n2) { return n1 + n2; } }
3.什么是異步方法
語法上,異步方法具有如下特點:
方法頭包含async方法修飾符,
包含一個或多個await表達式,
必須具備以下三種返回類型:Task<T>,Task,void,
異步方法的參數可以為任意類型的任意數量,但不能為out和ref參數,
按照約定,異步方法的名稱應該以Async為后綴,
除了方法以外,lambda表達式和匿名方法也可以作為異步對象,
使用返回Task<int>對象的異步方法
class Program { static void Main(string[] args) { Task<int> value = DoAsyncStuff.CalculateSumAsync(5, 6); Console.WriteLine("value:{0}", value.Result); Console.ReadKey(); } } static class DoAsyncStuff { public static async Task<int> CalculateSumAsync(int n1, int n2) { int sum = await Task.Run(() => GetSum(n1, n2)); return sum; } private static int GetSum(int n1, int n2) { return n1 + n2; } }
使用返回Task對象的異步方法
class Program { static void Main(string[] args) { Task value = DoAsyncStuff.CalculateSumAsync(5, 6); value.Wait(); Console.WriteLine("Async stuff is done"); Console.ReadKey(); } } static class DoAsyncStuff { public static async Task CalculateSumAsync(int n1, int n2) { int sum = await Task.Run(() => GetSum(n1, n2)); Console.WriteLine("sum:{0}", sum); } private static int GetSum(int n1, int n2) { return n1 + n2; } }
使用“調用並忘記”的異步方法
class Program { static void Main(string[] args) { DoAsyncStuff.CalculateSumAsync(5, 6); Thread.Sleep(200); Console.WriteLine("Program Exiting"); Console.ReadKey(); } } static class DoAsyncStuff { public static async void CalculateSumAsync(int n1, int n2) { int sum = await Task.Run(() => GetSum(n1, n2)); Console.WriteLine("sum:{0}", sum); } private static int GetSum(int n1, int n2) { return n1 + n2; } }