前段時間面試的時候被問到了非同步和多執行緒的區別,回答的模稜兩可,今天花時間系統的瞭解了下,如有錯誤,歡迎斧正~
非同步與多執行緒是在併發程式設計中兩個比較關鍵的概念, 很容易弄混淆:
非同步程式設計(Asynchronous)
概念:非同步程式設計是併發程式設計的一種形式,即在程式執行邏輯中,一部分語句可以獨立於主程式而執行。
理解:非同步相對於同步而言,同步的模式很容易理解,假設一塊程式只有十步邏輯,那麼這段邏輯中每步程式碼都要等待前一步程式碼執行完成才會執行,換句話說就是主程式流是從上往下依次順序執行的,如果遇到一個步驟,常見的是我們對後端一個請求或一次資料訪問,這個操作往往耗時很久(這個操作我們稱之為一個阻斷操作),整個頁面就會停在那裡沒有響應。為了提高頁面的體驗度,非同步請求應運而生。
使用目的:為了提高使用者體驗,最佳化頁面載入。
關鍵字:在C#中,非同步程式設計透過關鍵字async和await來達成。
多執行緒程式設計(Multithreading)
概念:多執行緒就是CPU利用多個執行緒來併發地執行多段邏輯。
理解:在電腦科學中,一個執行緒指的是在程式中一段連續的邏輯控制流。在業務很複雜的時候,一個執行緒無法滿足現有業務需求,多執行緒程式設計就應運而生。
使用目的:為了最大限度地利用多核CPU這個硬體條件來達到多個執行緒同時執行邏輯。
關鍵字:在C#中,System.Threading名稱空間下有很多類用來進行多執行緒程式設計。
非同步VS多執行緒
一個常見的概念混淆就是吧非同步和多執行緒當成一回事,其實不一樣。非同步側重於任務的執行順序,而多執行緒則是關於多個執行緒如何併發執行。應該說多執行緒是實現非同步的常用手段,但不能說他們是一回事。即便是隻有一個執行緒的情況下。我們仍然可以實現非同步。
舉例說明兩者的區別:
首先來看非同步程式設計的程式碼:
public static async Task FirstAsync() { Console.WriteLine($"第一個非同步方法開始,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); await Task.Delay(2000); Console.WriteLine($"第一個非同步方法結束,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); } public static async Task SecondAsync() { Console.WriteLine($"第二個非同步方法開始,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); await Task.Delay(2000); Console.WriteLine($"第二個非同步方法結束,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); } public static async Task ThirdAsync() { Console.WriteLine($"第三個非同步方法開始,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); await Task.Delay(2000); Console.WriteLine($"第三個非同步方法結束,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); }
我們這裡定義了三個非同步方法,在每個非同步方法中,我們在任務開始執行的時候在控制檯列印出執行緒ID,然後透過延時兩秒鐘來模擬業務邏輯,最後我們依然列印出執行緒ID。
然後,新增一個非同步方法來呼叫上面的非同步方法:
public static async Task ExecuteAysnc() { var firstAsync = FirstAsync(); var secondAsync = SecondAsync(); var thirdAsync = ThirdAsync(); await Task.WhenAll(firstAsync, secondAsync, thirdAsync); }
最後在Main方法中呼叫執行方法:
public static async Task ExecuteAysnc() { var firstAsync = FirstAsync(); var secondAsync = SecondAsync(); var thirdAsync = ThirdAsync(); await Task.WhenAll(firstAsync, secondAsync, thirdAsync); }
執行結果如下:
從執行結果可以看出:所有三個非同步方法都是線上程ID為1的執行緒上執行的,但是經過2秒鐘的睡眠,當再次執行非同步任務的時候,每個任務又被別的執行緒執行起來,這裡顯示為4、7、5.
為什麼會發生這樣的事情呢?原因就在於當執行緒發現關鍵字await的時候,該執行緒就會從當前邏輯釋放而返回給執行緒池。一旦睡眠結束,一個新的執行緒(4、7、5)就重新被分配來執行三個任務。
同樣地,我們再看看多執行緒的程式碼示例,與上面的例子不同,我們去除了關鍵字async
public static void FirstMethod() { Console.WriteLine($"第一個方法開始,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(2000); Console.WriteLine($"第一個方法結束,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); } public static void SecondMethod() { Console.WriteLine($"第二個方法開始,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(2000); Console.WriteLine($"第二個方法結束,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); } public static void ThirdMethod() { Console.WriteLine($"第三個方法開始,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(2000); Console.WriteLine($"第三個方法結束,執行的執行緒ID為:{Thread.CurrentThread.ManagedThreadId}"); }
邏輯和非同步方法的一樣,都是在開始的時候列印執行緒ID,2秒的業務邏輯操作之後再列印當前執行緒ID,同樣,我們建立一個方法,再這個方法裡,開啟三個執行緒來啟動這三個任務:
public static void ExecuteMultithreading() { Thread firstThread = new Thread(() => FirstMethod()); Thread secondThread = new Thread(() => SecondMethod()); Thread thirdThread = new Thread(() => ThirdMethod()); firstThread.Start(); secondThread.Start(); thirdThread.Start(); }
main函式呼叫執行方法:
static void Main(string[] args) { Multithreading.ExecuteMultithreading(); Console.ReadLine(); }
執行結果如下:
從列印結果可以看出,同一個方法,開始和結束的執行緒ID保持一致。再多執行緒的例子裡,我們顯示地分配了執行緒,該執行緒直到做完對應的任務才會結束,即時邏輯當中有一段時間的業務邏輯。
使用場景:
現在我們明白了非同步和多執行緒的差別,下面我們可以得出:
非同步程式設計適用場景:
- 我們的程式邏輯中存在阻斷操作。
- 我們的應用有一個執行緒池並且有垂直擴充套件的需求。
多執行緒程式設計適用場景:
- 我們的程式邏輯存在相互獨立的任務,任務與任務之間不相互干擾。
- 我們存在多個可用的CPU核心數。
總結:
非同步程式設計和多執行緒程式設計的區別在於側重點不同,非同步程式設計側重於任務的執行順序,而多執行緒程式設計則是關於多個執行緒如何併發執行最大限度利用CPU,兩者對於提高程式可用性和效能方面起到了關鍵性的作用。