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


一、簡介

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、數據庫訪問、中間件等等。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM