這幾天終於弄懂了async和await的模式,也搞明白了一直在心里面積壓着的許多問題,所以寫一篇博客來和大家分享一下。
關於異步機制我認為只要記住的以下幾點,就可以弄明白了:
1.我認為async和awwait兩個修飾符中最關鍵的是await,async是由於方法中包含await修飾符之后才在方法定義中添加的,表明這個方法是一個異步方法。
2.await只能用來修飾Task、Task<TResult>、ValueTask 或 ValueTask<TResult>這些類型的變量或者方法,他是一個分裂符,我們需要記住的最關鍵一點是當程序進行到await的時候,方法會暫時返回,而await字符之后的內容會等await這一行返回后繼續執行。
3.在控制台應用中,await之前的內容是一個線程執行,await以及awiat之后的內容會在另一個線程中執行。
我們在編程時很多時候其實都是單行寫的await,尤其是在主方法和mvc的控制器中,這也導致我一開始學習的時候對await產生了非常多的疑問,以至於一直都沒有弄清楚,比如:
int bytesLoaded = await DownloadDocsMainPageAsync();
這個幾乎是我們寫代碼時見到最多的用法了,在主方法中使用await 來做一些request的請求,當時就在想await不是異步嗎,但為什么還要在這里等待這個輸出才進行下一步的執行呢?那么異步是異步到哪里了呢?其實這種使用方式並沒有體現出異步的特色,只是因為request中很多方法是異步方法而為了獲取結果才這么寫的,便於理解的方式其實是下面這樣的:
Task<int> downloading = DownloadDocsMainPageAsync(); //省略代碼,這里有一些處理方式// int bytesLoaded = await downloading;
關於如何使用,感謝評論中的同學指出,其實並不是上面的方式就是正確的使用方式,之前的寫法太主觀了,這種寫法我覺得更便於我們理解。await之前都是同一個線程執行的,所以多行寫還是單行寫無所謂效率問題,如果是嵌套在方法中使用,單行寫法可能更准確一些(更能表達出在執行這個方法時開始異步)。
異步是在我們處理一件事的時候可以同時進行着另一件事,下圖說明了上面代碼在主方法中的調用邏輯:
另外我寫了一個異步的demo,這個方法里面有兩個異步的例子,大家有興趣的話可以在本地自己調試去感受一下異步的實現方式和調用的順序,方便大家理解:
//private static void Main(string[] args) //{ // //Console.WriteLine("111 balabala. My Thread ID is :" + Thread.CurrentThread.ManagedThreadId); // //var r = AsyncMethod(); // //Console.WriteLine(r); // //Console.WriteLine("222 balabala. My Thread ID is :" + Thread.CurrentThread.ManagedThreadId); // //Thread.Sleep(10000); //} public static async Task Main() { Task<int> downloading = DownloadDocsMainPageAsync(); Console.WriteLine($"{nameof(Main)}: Launched downloading."); Console.WriteLine("main1 " + Thread.CurrentThread.ManagedThreadId); int bytesLoaded = await downloading; Console.WriteLine("main2 " + Thread.CurrentThread.ManagedThreadId); Console.WriteLine($"{nameof(Main)}: Downloaded {bytesLoaded} bytes."); } private static async Task<int> DownloadDocsMainPageAsync() { Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: About to start downloading."); Console.WriteLine("sub1 " + Thread.CurrentThread.ManagedThreadId); var client = new HttpClient(); byte[] content = await client.GetByteArrayAsync("https://docs.microsoft.com/en-us/"); Console.WriteLine("sub2 " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(5000); Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: Finished downloading."); return content.Length; } private async static Task<String> AsyncMethod() { var ResultFromTimeConsumingMethod = TimeConsumingMethod(); string Result = await ResultFromTimeConsumingMethod + " + AsyncMethod. My Thread ID is :" + Thread.CurrentThread.ManagedThreadId; Console.WriteLine(Result); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); return Result; //返回值是Task的函數可以不用return } //這個函數就是一個耗時函數,可能是IO操作,也可能是cpu密集型工作。 private static Task<string> TimeConsumingMethod() { var task = Task.Run(() => { Console.WriteLine("Helo I am TimeConsumingMethod. My Thread ID is :" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(5000); Console.WriteLine("Helo I am TimeConsumingMethod after Sleep(5000). My Thread ID is :" + Thread.CurrentThread.ManagedThreadId); return "Hello I am TimeConsumingMethod"; }); return task; }
最后附上我參考的一些內容:
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/await
上面demo有一個被注釋了的main方法是我參考的另一個大神的博客,正是讀了他的博客才讓我恍然大悟(雖然里面的其他敘述也不是非常准確),但是在理解async和await方面,這篇博客真正點醒了我,很可惜我當時讀博客的時候並沒有收藏,后面也沒有找到博客地址,但是還是要在這里感謝他。