淺談C#中的Async和Await的用法詳解
眾所周知C#提供Async和Await關(guān)鍵字來實(shí)現(xiàn)異步編程。在本文中,我們將共同探討并介紹什么是Async 和 Await,以及如何在C#中使用Async 和 Await。
同樣本文的內(nèi)容也大多是翻譯的,只不過加上了自己的理解進(jìn)行了相關(guān)知識(shí)點(diǎn)的補(bǔ)充,如果你認(rèn)為自己的英文水平還不錯(cuò),大可直接跳轉(zhuǎn)到文章末尾查看原文鏈接進(jìn)行閱讀。
寫在前面
自從C# 5.0時(shí)代引入async和await關(guān)鍵字后,異步編程就變得流行起來。尤其在現(xiàn)在的.NET Core時(shí)代,如果你的代碼中沒有出現(xiàn)async或者await關(guān)鍵字,都會(huì)讓人感覺到很奇怪。
想象一下當(dāng)我們?cè)谔幚鞺I和按鈕單擊時(shí),我們需要運(yùn)行一個(gè)長時(shí)間運(yùn)行的方法,比如讀取一個(gè)大文件或其他需要很長時(shí)間的任務(wù),在這種情況下,整個(gè)應(yīng)用程序必須等待這個(gè)長時(shí)間運(yùn)行的任務(wù)完成才算完成整個(gè)任務(wù)。
換句話說,如果同步應(yīng)用程序中的任何進(jìn)程被阻塞,則整個(gè)應(yīng)用程序?qū)⒈蛔枞?,我們的?yīng)用程序?qū)⑼V鬼憫?yīng),直到整個(gè)任務(wù)完成。
在這種情況下,異步編程將非常有用。通過使用異步編程,應(yīng)用程序可以繼續(xù)進(jìn)行不依賴于整個(gè)任務(wù)完成的其他工作。
在Async 和 await關(guān)鍵字的幫助下,使得異步編程變得很簡單,而且我們將獲得傳統(tǒng)異步編程的所有好處。
實(shí)例講解
假設(shè)我們分別使用了兩種方法,即Method 1和Method 2,這兩種方法不相互依賴,而Method 1需要很長時(shí)間才能完成它的任務(wù)。在同步編程中,它將執(zhí)行第一個(gè)Method 1,并等待該方法的完成,然后執(zhí)行Method 2。因此,這將是一個(gè)時(shí)間密集型的過程,即使這兩種方法并不相互依賴。
我們可以使用簡單的多線程編程并行運(yùn)行所有方法,但是它會(huì)阻塞UI并等待完成所有任務(wù)。要解決這個(gè)問題,我們必須在傳統(tǒng)編程中編寫很多的代碼,但是現(xiàn)在我們有了Async 和 await關(guān)鍵字,那么我們將通過書寫很少的并且簡潔的代碼來解決這個(gè)問題。
此外,我們還將看到更多的示例,如果任何第三個(gè)方法(如Method 3)都依賴于Method 1,那么它將在Wait關(guān)鍵字的幫助下等待Method 1的完成。
Async 和 await是代碼標(biāo)記,它標(biāo)記代碼位置為任務(wù)完成后控件應(yīng)該恢復(fù)的位置。
下面讓我們舉幾個(gè)例子來更好進(jìn)行理解吧
C#中Async 和 await關(guān)鍵字的示例
我們將采用控制臺(tái)應(yīng)用程序進(jìn)行演示。
第一個(gè)例子
在這個(gè)例子中,我們將采取兩個(gè)不相互依賴的方法。
class Program { static void Main(string[] args) { Method1(); Method2(); Console.ReadKey(); } public static async Task Method1() { await Task.Run(() => { for (int i = 0; i < 100; i++) { Console.WriteLine(" Method 1"); } }); } public static void Method2() { for (int i = 0; i < 25; i++) { Console.WriteLine(" Method 2"); } } }
在上面給出的代碼中,Method 1和Method 2不相互依賴,我們是從主方法調(diào)用的。
在這里,我們可以清楚地看到,方法1和方法2并不是在等待對(duì)方完成。
輸出
現(xiàn)在來看第二個(gè)例子,假設(shè)我們有Method 3,它依賴于Method 1
第二個(gè)例子
在本例中,Method 1將總長度作為整數(shù)值返回,我們?cè)贛ethod 3中以長度的形式傳遞一個(gè)參數(shù),它來自Method 1。
在這里,在傳遞Method 3中的參數(shù)之前,我們必須使用AWAIT關(guān)鍵字,為此,我們必須使用調(diào)用方法中的async 關(guān)鍵字。
在控制臺(tái)應(yīng)用程序的Main方法中,因?yàn)椴荒苁褂胊sync關(guān)鍵字而不能使用await 關(guān)鍵字,因?yàn)樗鼤?huì)給出下面給出的錯(cuò)誤。(但是如果你使用的是C#7.1及以上的方法是不會(huì)有問題的,因?yàn)镃#7.1及以上的語法支持Mian方法前加async)
我們將創(chuàng)建一個(gè)新的方法,作為CallMethod,在這個(gè)方法中,我們將調(diào)用我們的所有方法,分別為Method 1、Method 2和Method 3。
class Program { static void Main(string[] args) { callMethod(); Console.ReadKey(); } public static async void callMethod() { Task<int> task = Method1(); Method2(); int count = await task; Method3(count); } public static async Task<int> Method1() { int count = 0; await Task.Run(() => { for (int i = 0; i < 100; i++) { Console.WriteLine(" Method 1"); count += 1; } }); return count; } public static void Method2() { for (int i = 0; i < 25; i++) { Console.WriteLine(" Method 2"); } } public static void Method3(int count) { Console.WriteLine("Total count is " + count); } }
在上面給出的代碼中,Method 3需要一個(gè)參數(shù),即Method 1的返回類型。在這里,await關(guān)鍵字對(duì)于等待Method 1任務(wù)的完成起著至關(guān)重要的作用。
輸出
第三個(gè)例子
.NET Framework4.5中有一些支持API,Windows運(yùn)行時(shí)包含支持異步編程的方法。
在Async 和 await關(guān)鍵字的幫助下,我們可以在實(shí)時(shí)項(xiàng)目中使用所有這些,以便更快地執(zhí)行任務(wù)。
包含異步方法的API有HttpClient, SyndicationClient, StorageFile, StreamWriter, StreamReader, XmlReader, MediaCapture, BitmapEncoder, BitmapDecoder 等。
在本例中,我們將異步讀取大型文本文件中的所有字符,并獲取所有字符的總長度。
class Program { static void Main() { Task task = new Task(CallMethod); task.Start(); task.Wait(); Console.ReadLine(); } static async void CallMethod() { string filePath = "E:\\sampleFile.txt"; Task<int> task = ReadFile(filePath); Console.WriteLine(" Other Work 1"); Console.WriteLine(" Other Work 2"); Console.WriteLine(" Other Work 3"); int length = await task; Console.WriteLine(" Total length: " + length); Console.WriteLine(" After work 1"); Console.WriteLine(" After work 2"); } static async Task<int> ReadFile(string file) { int length = 0; Console.WriteLine(" File reading is stating"); using (StreamReader reader = new StreamReader(file)) { // Reads all characters from the current position to the end of the stream asynchronously // and returns them as one string. string s = await reader.ReadToEndAsync(); length = s.Length; } Console.WriteLine(" File reading is completed"); return length; } }
在上面給出的代碼中,我們調(diào)用ReadFile方法來讀取文本文件的內(nèi)容,并獲取文本文件中總字符的長度。
在sampleText.txt中,文件包含了太多的字符,因此讀取所有字符需要很長時(shí)間。
在這里,我們使用異步編程從文件中讀取所有內(nèi)容,所以它不會(huì)等待從這個(gè)方法獲得一個(gè)返回值并執(zhí)行其他代碼行,但是它必須等待下面給出的代碼行,因?yàn)槲覀兪褂玫氖堑却P(guān)鍵字,我們將對(duì)下面給出的代碼行使用返回值。
int length = await task; Console.WriteLine(" Total length: " + length);
隨后,將按順序執(zhí)行其他代碼行。
Console.WriteLine(" After work 1"); Console.WriteLine(" After work 2");
輸出
最后
在這里,我們必須了解非常重要的一點(diǎn),如果我們沒有使用await 關(guān)鍵字,那么該方法就作為一個(gè)同步方法。編譯器將向我們顯示警告,但不會(huì)顯示任何錯(cuò)誤。
像上面這種簡單的方式一樣,我們可以在C#代碼中使用async 和await關(guān)鍵字來愉快的進(jìn)行異步編程了。
最后的最后感謝大家的閱讀!希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
講解.NET環(huán)境下繪制模糊數(shù)學(xué)中隸屬函數(shù)分布圖
講解.NET環(huán)境下繪制模糊數(shù)學(xué)中隸屬函數(shù)分布圖...2007-11-11Unity實(shí)現(xiàn)毫秒延時(shí)回調(diào)功能
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)毫秒延時(shí)回調(diào)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Repeater中添加按鈕實(shí)現(xiàn)點(diǎn)擊按鈕獲取某一行數(shù)據(jù)的方法
這篇文章主要介紹了Repeater中添加按鈕實(shí)現(xiàn)點(diǎn)擊按鈕獲取某一行數(shù)據(jù)的方法,是非常實(shí)用的一個(gè)技巧,需要的朋友可以參考下2014-08-08C#使用Socket快速判斷數(shù)據(jù)庫連接是否正常的方法
這篇文章主要介紹了C#使用Socket快速判斷數(shù)據(jù)庫連接是否正常的方法,涉及C#中socket操作的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04C#使用linq計(jì)算執(zhí)行元素在列表中出現(xiàn)次數(shù)的方法
這篇文章主要介紹了C#使用linq計(jì)算執(zhí)行元素在列表中出現(xiàn)次數(shù)的方法,涉及C#使用linq擴(kuò)展進(jìn)行列表查詢的技巧,需要的朋友可以參考下2015-04-04