C#進(jìn)階之Async await異步編程

前言

自從C# 5.0時代引入async和await關(guān)鍵字后,異步編程就變得流行起來。尤其在現(xiàn)在的.NET Core時代,甚至如果你的代碼中沒有出現(xiàn)async或者await關(guān)鍵字,都會讓人感覺到很奇怪。
本篇文章將會先給大家?guī)硪粋€關(guān)于Async簡單的例子,然后再四種啟動線程的方法的對比中讓大家意識到為什么現(xiàn)在的大佬都偏愛Async await.
在這之前,我希望大家首先能知道下面這個事實:

// < : 后者性能高于前者
Thread < ThreadPoll < Task < Async await
注:上述情況只在頻繁使用線程的情況下適用

Async await

方法使用Async修飾符修飾.
返回類型僅有三種: void,Task,Task<T>
方法內(nèi)部使用await關(guān)鍵字標(biāo)明開始執(zhí)行異步代碼
await標(biāo)志前的代碼是同步執(zhí)行,await標(biāo)志的方法是異步執(zhí)行,await標(biāo)志的方法后面的代碼相當(dāng)于"回調(diào)函數(shù)",在await標(biāo)志的異步方法后面執(zhí)行.
所以使用Async await異步編程之后代碼的執(zhí)行順序會變成下面這樣:


Async await編程代碼執(zhí)行順序.png

如果沒看懂上圖,也沒關(guān)系,我們再來看一個小例子:
下面?zhèn)€例子中,我用英文的輸出代表主線程的輸出,中文的輸出代表子線程的輸出

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Let`s Do This!");
            Console.WriteLine("I am going to call my son.");
            AsyncTask();//調(diào)用Async修飾的方法
            Console.WriteLine("My son is busy now,and I will go on.");
            Console.WriteLine("I`m done!");
            Console.ReadLine();
        }

       //Async修飾的方法
        public async Task AsyncTask()
        {
            Console.WriteLine($"我是方法AsyncTask");
            var result= await WasteTime();
            Console.WriteLine(result);
            Console.WriteLine("我已經(jīng)干完了我應(yīng)該干的事情!");
        }

        private async Task<string> WasteTime()
        {
            return await Task.Run(() =>
            {
                Thread.Sleep(1);//避免Console.WriteLine執(zhí)行太快使整個程序執(zhí)行起來像是同步執(zhí)行的
                Console.WriteLine("我開始異步執(zhí)行了!");
                Console.WriteLine("可是我啥都不想干,還是等個五秒鐘就跟主線程說我做好了吧");
                Thread.Sleep(5000);
                return "我異步執(zhí)行完了";
            });
        }
    }

運行效果如下:


運行效果.png

為什么Thread < ThreadPoll

Thread默認(rèn)創(chuàng)建的是前臺線程,每次初始化一個Thread的示例,就會創(chuàng)建一個新的線程!
ThreadPoll默認(rèn)創(chuàng)建的是后臺線程,每次啟用線程會從線程池尋找空閑的線程,如果找到了,就會調(diào)用這個空閑的線程,而不是直接創(chuàng)建一個新的的線程.
而每次創(chuàng)建一個線程,起碼約消耗1M的內(nèi)存.
因此在需要開啟大量線程的情況下,相比于Thread,ThreadPoll具有減少開啟新線程消耗的資源,以及統(tǒng)一管理線程的優(yōu)勢!

為什么ThreadPoll < Task

ThreadPool使用的是線程池全局隊列,全局隊列中的線程依舊會存在競爭共享資源的情況,從而影響性能。
Task基于ThreadPool實現(xiàn),相當(dāng)于ThreadPoll的優(yōu)化版.它不再使用線程池的全局隊列,而是使用的本地隊列,使線程之間的資源競爭減少。同時Task提供了豐富的API來管理線程、控制。但是相對前面的兩種耗內(nèi)存,Task依賴于CPU對于多核的CPU性能遠(yuǎn)超前兩者,單核的CPU三者的性能沒什么差別。

為什么Task < Async await

1.舉個例子:
現(xiàn)在需要實現(xiàn)一個,異步寫數(shù)據(jù)庫,寫成功之后需要在頁面上彈出一個小窗提示.
如果你用task.result來實現(xiàn)的話,那么在獲取到結(jié)果之前,task會使你的項目進(jìn)去一個阻塞狀態(tài).也就是說在小窗彈出來之前,你的程序會一直卡主!從而形成異步編程中的同步阻塞
而Async await則不會發(fā)生這種情況.你需要做的僅僅只是在await修飾的方法后面調(diào)一個彈出小窗的方法而已.也就是傳說中的異步阻塞

2.Async await風(fēng)格的編程會使代碼看起來更像是同步代碼,也就更像清新移動更通俗.

結(jié)語

其實在你理解了異步編程的基礎(chǔ)上,Async await并沒有什么復(fù)雜的.明白了例子中帶返回值的寫法,那別的兩種返回類型應(yīng)該也是信手拈來.從表面上看來,它的編碼風(fēng)格跟同步方法非常相似,以至于新手看到會有一種"我*,這不是同步方法嗎"的感覺,從而給心理上造成了一定負(fù)擔(dān),使自己覺得Async很難,所以懶得去研究了.

最后,如果本篇文章給您帶來了幫助,就點個贊再走唄~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容