iT邦幫忙

0

筆記:.NET 內的生產者消費者模式 — BlockingCollection and Channel

  • 分享至 

  • xImage
  •  

筆記:.NET 內的生產者消費者模式 — BlockingCollection and Channel

如果要在同一個 process 內實作「生產者與消費者(Producer-Consumer)」模式,最簡單的方式就是直接使用 .NET 內建的類別即可。


BlockingCollection(同步)

早期的 生產者 & 消費者 模式的物件。

  • 優點

    • 是 thread-safe 的物件。就算有多個生產者呼叫 Add 與多個消費者呼叫 GetConsumingEnumerable,底層都已經是實作成 Thread-safe 的了。
    • 可以設定上限容量。當消費者來不及處理完生產者的 job 時,可以先把生產者擋下,不再讓更多的 job 流入。可以避免將更源頭的 job 一次全部載進 local process 內,進而導致記憶體問題,或是 process 突然故障導致這些 in memory 的 job 遺失。
  • 缺點

    • 由於它是同步的實作。所以當 queue 裡面沒有任何 job 要做的時候,消費者的那條 thread 就會直接閒置在那邊,一直等到有新 job 或是結束的時候。
  • 個人經驗

在舊系統的架構裡蠻常看到像 Windows Service 這種背景處理程式實作這種模式。如果這個 service 很輕量(單純的只做某一、二個職責工作),那只要把 server core 數量跟生產者的 worker 數量調配好,可以算是一個簡單粗暴的實作。


System.Threading.Channels(非同步)

隨者 async/await 的非同步實作成為主流,後來 .net 就再推出了 System.Threading.Channels

  • 優點

    • 已經轉為非同步處理。消費者可以使用 foreach 配上 ReadAllAsync 方法,即可方便的實作出非同步的消費機制。或是用 WaitToReadAsync 先等待訊號,再用 TryRead 批次取出來處理。這代表當沒有 Job 需要處理的時候,thread 會先被還回去 thread pool,然後去處理別的事情,而不會整個閒置在那邊。
    • 也可設定上限容量,讓 queue 裡的 job 滿的時候,選擇要"拋棄" or "覆蓋"的模式。
    • 在使用 channel 的時候,可以透過 ChannelOptions 的來設定 SingleWriterSingleReader,也就是可以直接告訴程式是否只有「單一個」生產者 or 消費者。好處是在 enqueue or dequeue 的時候,底層就「不用」再去考慮 thread-safe 的問題(因為只有一條 thread 在做),那就可以省掉一些麻煩的上鎖機制,能夠在效率更好的情形下吃更多的量。
  • 個人經驗

在新實作的功能或專案裡,儘量都直接以 Channel 來進行實作。因為在一樣的前提之下,「理論」上 Channel 應該都會比 BlockingCollection 能夠吃下更多的量。


參考資料
筆記:.NET 併發處理 Async/Await 筆記
現在 C#:AI 時代的開發者修煉
Concurrency in C# Cookbook


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言