.Net Core中無處不在的Async/Await是如何提升效能的?

包子wxl發表於2022-03-07

一、簡介

Async/Await在.Net Core中真的是無處不在,到處都是非同步操作,那為什麼要用?有什麼作用?別人說能提升效能?網上一堆文章看的繞暈了也沒說清楚,

所以這裡從理論,實踐,原理一個個解開這些疑問。

二、Async/Await有什麼用?

1.Async/Await用法示例

用法很簡單,這裡就不詳細說具體怎麼用了,只提供一個示例,我們的目標是研究它的作用。

 public class AsyncAwaitTest
    {
        public void Start()
        {
            Console.WriteLine($"aaa,執行緒Id:{Thread.CurrentThread.ManagedThreadId}");
            AsyncMethod();
            Console.WriteLine($"eee,執行緒Id:{Thread.CurrentThread.ManagedThreadId}");
            Console.ReadLine();
        }
        public async Task<bool> AsyncMethod()
        {
            Console.WriteLine($"bbb,執行緒Id:{Thread.CurrentThread.ManagedThreadId}");
            await Task.Run(() => {

                Thread.Sleep(500);
                Console.WriteLine($"ccc,執行緒Id:{Thread.CurrentThread.ManagedThreadId}");
            });
            Console.WriteLine($"ddd,執行緒Id:{Thread.CurrentThread.ManagedThreadId}");
            return true;
        }
    }

 

 

2.async/await的特點

1)不會阻塞執行緒

從示例的執行順序,可以看出,當執行async/await方法時,主執行緒遇到await關鍵字,主執行緒就返回執行“eee”,然後釋放,而不是等待,新開了一個子執行緒6執行另外的業務,await前面的方法還是主執行緒執行,await後面的方法,等執行緒6執行完了再繼續執行。

2)同步的方式寫非同步

雖然是用了非同步,但還是等待執行結果再往下執行,執行流程是同步的。

3.async/await能提升效能嗎?

這個應該是大家最關心的問題了。

能提升單個請求的效能嗎?

答案是不能的。很明顯看的到,await等待了結果再執行後面的邏輯,還是序列的,執行完該多少秒還是多少秒, 中間還切換執行緒去處理了,相比同步來說還多了切換執行緒的損耗。

那async/await的意義何在?

在於多請求併發處理,且資源有限的時候,能增加吞吐量(單位時間處理的請求),增加cpu的利用率。

簡單說就是有10個執行緒,每個執行緒的速度沒有提升,然後居然QPS能提升?!

先來看一段微軟官網的描述

  此模型可很好地處理典型的伺服器方案工作負荷。由於沒有專用於阻止未完成任務的執行緒,因此伺服器執行緒池可服務更多的Web請求。

  考慮使用兩個伺服器:一個執行非同步程式碼,一個不執行非同步程式碼。對於本例,每個伺服器只有5個執行緒可用於伺服器請求。此字數太小,不切實際,僅供演示。

  假設這兩個伺服器都接收6個併發請求。每個請求執行一個I/O操作。未執行非同步程式碼的伺服器必須對第6個請求排隊,直到5個執行緒中的一個完成了I/O密集型工作

並編寫了響應。此時收到了第20個請求,由於佇列過長,伺服器可能會開始變慢。

  執行有非同步程式碼的伺服器也需要對第6個請求排隊,但由於使用了async和await,I/O密集型工作開始時,每個執行緒都會得到釋放,無需等到工作結束。

收到第20個請求時,傳入請求佇列將變得很小(如果其中還有請求的話),且伺服器不會慢。

  儘管這是一個人為想象的示例,但現實世界中其工作方式與此類似。事實上,相比伺服器將執行緒專用於接收到的每個請求,使用async和await能夠使

伺服器處理一個數量級的請求。

 

  注意上面官網描述的I/O密集型。什麼樣的是I/O密集型呢?,就是cpu效能比硬碟記憶體好太多,大部分時間都是cpu在等IO的讀寫操作。例如讀檔案,讀檔案的時候是不需要cpu參與的,只需要發一個命令給硬碟,硬碟讀完檔案會再通知cpu繼續處理,這種叫DMA技術

  DMA(Direct Memory Access,直接儲存器訪問) 是所有現代電腦的重要特色,它是指一種高速的資料傳輸操作,允許在外部裝置和儲存器之間直接讀寫資料,既不通過cpu,也不需要cpu干預。

  這個時候非同步就顯出它的優勢來了,比如讀檔案需要1s,如果是同步操作,那麼就有一個執行緒在等1s再往下執行。如果是非同步的,讀檔案的時候,這個執行緒就釋放了,等讀完檔案,硬碟通知cpu再派一個執行緒接著處理,那中間的1秒,原來的執行緒就可以去處理其他請求了。

4.程式碼對照說明

public class HomeController : Controller
    {
        /// <summary>
        /// 同步請求
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public string GetData()
        {
            var result = System.IO.File.ReadAllBytes(@"F:\package\package.rar");
            return "ok";
        }

        /// <summary>
        /// 非同步請求
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public async Task<string> GetDataAsync2()
        {
            var result = await System.IO.File.ReadAllBytesAsync(@"F:\package\package.rar");
            return "ok";
        }
    }

 

同步請求的流程為

 可以看出,硬碟在讀取檔案時,執行緒是在等待的,這時候執行緒1在這1s中是不工作的,空等狀態。

非同步請求的流程為

 非同步請求時,執行緒1遇到await關鍵字,發出命令就返回,然後釋放掉了,硬碟讀完資料會通知cpu,這時cpu派一個新的執行緒去接著處理,

因此,讀檔案的這1s,執行緒1可以去處理其它請求了,沒有空等,這就是提高了cpu的利用率,單位時間內處理的請求數就變大了。

 

cpu密集型的非同步是不能提高QPS的,下面程式碼就是cpu密集型的。

cpu密集型:計算密集型,硬碟、記憶體效能比cpu好很多,或不太需要訪問I/O裝置。

     /// <summary>
        /// 非同步請求
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public async Task<string> GetDataAsync2()
        {
            await Task.Run(() => {
                Thread.Sleep(100);//模擬業務處理耗時
            });
            return "ok";
        }

這裡前面主執行緒遇到await雖然釋放了,但await裡面又有一個執行緒接著工作,cpu(執行緒)並沒有空閒

I/O密集型中IO的操作有哪些呢?

檔案讀寫、http請求、資料庫請求、redis請求。。。等等。

 開發中哪些推薦用非同步呢?

Web開發推薦、有Async的API的,Action、Filter、資料庫訪問、中介軟體等等。。。

 

相關文章