歡迎光臨
每天分享高質量文章

C#併發程式設計之非同步程式設計(一)

寫在前面

C#5.0中,對非同步程式設計進行了一次革命性的重構,引入了async和await這兩個關鍵字,使得開發人員在不需要深刻瞭解非同步程式設計的底層原理,就可以寫出十分優美而又程式碼量極少的程式碼。如果使用得當,你可以寫出具有並行化並且效能較高的程式,但是同時也增加了對非同步程式設計理解的複雜度,畢竟在C#5.0裡,你已經看不到非同步程式設計具體是如何實現的了,需要花費額外的經歷去研究探索。

使用非同步程式設計,使得我們釋放了啟動它的執行緒,也使得資源的佔有量下降。更重要的是,有些特殊執行緒,比如UI執行緒,在執行的時候只能啟動一個,如果沒有快速響應,頁面將會出現卡頓現象。本文只會基於.NET FX4.5及以後的版本進行講解,之前的版本如果要實現非同步程式設計,需要從nuget上面下載Microsoft.Bcl.Async,不過我還是建議你,如果想要在系統中大量使用編寫非同步程式碼,還是要是使用.NET FX4.5或更高的版本

非同步程式設計主要分為基於事件的非同步樣式(EAP)和基於任務的程式設計樣式(TAP)。EAP在呼叫方法之前立即註冊事件,它具有void傳回型別,但這種樣式比較混亂,它將原本的一個方法分拆成兩個方法。本系列主要關註TAP程式設計而不涉及EAP程式設計。

非同步程式設計是什麼

非同步關鍵字

作為C#5.0中新增的重量級功能,非同步功能是指程式在進行長時間操作完成後,需要繼續執行的操作的一種方法,在程式設計過程中,會感覺這些非同步程式碼和同步或者阻塞程式碼類似,但是實際上,編譯器會將標識為非同步的方法進行進一步的轉換,是的程式碼可以實現真正的非同步程式設計。它主要以兩個關鍵字的形式功能大家使用:

  • async
  • await

以下以一個透過EF Core查詢使用者資訊的程式碼片段,這個例子沒有什麼特殊的地方

public Users GetUserInfo(string userId)
{
    using (UserDbContext db = new UserDbContext())
    {
        var user = db.Users.FirstOrDefault(p => p.UserId = userId);
        return user;
    }
}

接下來我們看看非同步的實現程式碼

public async Task GetUserInfoAsync(string userId)
{
    using (UserDbContext db = new UserDbContext())
    {
        var user = await db.Users.FirstOrDefaultAsync(p => p.UserId = userId);
        return user;
    }
}

以上兩段程式碼看起來非常的類似,但是仔細看卻有明顯的不同。非同步方法上多了一個async的標識,同時傳回值User,被標識成了Task,同時在進行資料庫查詢的時候,使用到了await。這裡提前說一下await關鍵字,當編譯器看到await關鍵字的時候,會截斷方法,便於執行緒排程。簡單點說,就是當呼叫執行緒執行到FirstOrDefaultAsync時,查詢開始,但不是在當前執行緒,在新的執行緒裡面,我們查詢完資料庫後,根據需要做進一步處理,比如,如果原執行緒UI執行緒,它將傳回以繼續處理使用者的其他操作(這裡非常類似回呼方式),否則的話,這個執行緒就直接被釋放了。這段可能比較抽象,會在之後的系列裡進一步講解。

為了更好的進行非同步程式設計,我們需要在方法簽名後面追加Async,這是一種非同步程式設計的規約,也希望大家遵守。

雖然非同步程式設計對系統以及使用者的體驗非常的有幫助,但如果對非同步程式設計不甚瞭解,可能會發生一些令人感到詭異的問題,而且這些問題可能透過debug方式也很難得到解決。

非同步執行流程

1、想象一下,在現實世界中,一個顧客到電腦專賣店買東西,就是那種拿了就走的場景。如果店鋪只有一個人,在與顧客1沒有結算完成之前,對顧客2的請求,只能暫時放置一下。相信大家在現實世界中,肯定會遇到類似的情況,心情可能也很不爽,如果不是很迫切,可能是再看看,換一家店,如果比較著急,就會一直催,然後也不一定會有回應。

如下圖所示

2、有一天,老闆請了幾個夥計幫忙搬電腦,在顧客1沒有結算完成之前,老闆就可以接住顧客2的需求,並透過資訊系統或者大吼一嗓子的方式,讓電腦準備顧客2的電腦。同時,電腦把顧客1的電腦搬到前臺,由老闆去跟顧客結算,整個的流程就顯得體驗度很高,顧客也不會被忽略,賣出去的東西也多了很多,不過等待還是要等的。

如下圖所示

寫在後面

本文主要介紹了非同步程式設計的基礎,透過以上介紹,我們知道要建立一個非同步函式,首先需要用async去修飾一個方法,同時傳回值型別必須是Task或者Task,當然在使用UI控制器時間處理的時候是可以使用async void的。在方法內部,需要使用await關鍵字。非同步函式會被編譯器編譯成複雜的程式結構,可以視其為一種狀態機。不過需要提醒的是,如果不需要編寫非同步函式,那就用同步。

雖然非同步程式設計已經變得非常簡單,但是大家同樣需要瞭解非同步程式設計背後的理念以及原理,這有助於我們編寫高效能高擴充套件的應用程式。

贊(0)

分享創造快樂