1. 程式人生 > 實用技巧 >多執行緒併發如何高效實現生產者/消費者?

多執行緒併發如何高效實現生產者/消費者?

前言

無需引入第三方訊息佇列元件,我們如何利用內建C#語法高效實現生產者/消費者對資料進行處理呢?在.NET Core共享框架(Share Framework)引入了通道(Channel),也就是說無需額外通過NuGet包安裝,若為.NET Framework則需通過NuGet安裝,前提是版本必須是4.6+(包含4.6),查詢網上資料少的可憐,估計也有部分童鞋都沒聽說這玩意,所以接下來將通過幾篇文章詳細介紹其使用和底層具體實現原理

生產者/消費者概念

生產者/消費者這一概念,相信我們大家都不陌生,在日常生活無處不在、隨處可見,其本質可用一句話概括:具有多個連續步驟的工作流程。比如美團外賣、再比如工廠裡面的流水作業線、又比如線下實體快餐店等等。整個過程如同一條鏈,在這個鏈中每個步驟必須被完全隔離執行,生產者產生“東西”,然後對其交由下一步驟進行處理,最終到達消費者。

上述敘述為一切抽象,我們回到軟體領域,在軟體中每一塊都在對應的執行緒中執行,以確保資料能得到正確處理,當然,這也就包括跨執行緒共享資料可能引起的併發問題。未出現該庫之前,我們可利用內建BlockingCollection實現生產者/消費者機制,但依然無法解決我們所面臨的兩個問題:其一:阻塞問題,其二:無任何基於Task的非同步APi執行非同步操作。通過引入System.Threading.Channel庫則可以完美解決生產者/消費者問題,毫無疑問,執行緒安全是前提,效能測試有保證,非同步提高吞吐量,配置選項夠靈活。目前來看,利用通道可能將是實現生產者/消費者的最終手段

通道(Channel)概念

名為通道還是比較形象,如同管道一樣,說到底就是執行緒安全的佇列,既然是佇列,那麼勢必涉及邊界問題,通道型別分為有界通道和無界通道。

有界通道(Bounded Channel):對傳入資料具有指定容量,這也就意味著,若生產者產生的資料一旦達到容量空間,將不得不等待消費者執行完為生產者推送資料騰出額外可用空間

無界通道:(Unbounded Channel):對傳入資料無上限,這也就意味著生產者可以持續不斷髮布資料,以此希望消費者能跟上生產者的節奏

到這裡我們完全可得出一結論:因通道提供有界和無界選項,所以內建不可能利用併發佇列來實現,一定是通過連結串列資料結構實現佇列機制。那麼問題來了,全部指定為無界通道豈不萬事大吉,這個問題想想就有問題,雖說無界通道為毫無上限,但計算機的系統記憶體不是,無論是有界通道抑或是無界通道都會通過快取區來儲存資料。所以選擇正確的通道型別,取決於業務上下文。那麼問題又來了,若建立有界通道,一旦達到容量限制,通道應該如何處理呢?別擔心,這個事情則交由我們根據實際業務情況來處理,邊界通道容量滿模式(BoundedChannelFullMode)列舉