進階並行與非同步

通道與 select(channels and select)

通道是並行任務之間的一根管子,像兩個工作站之間的輸送帶:一側把物件放上去,另一側把它們取下來,輸送帶本身負責安全的交接,使兩名工人永遠不必碰到彼此的手。在程式碼裡,通道是一個有兩端的執行緒安全佇列——一個發送端、一個接收端——用來把值從一個任務傳到另一個任務。它正是「不要靠共享記憶體來溝通;要靠溝通來共享記憶體」背後的具體工具:不讓兩個任務拿著鎖去爭一個共享變數,而是一個任務把值送進通道、另一個把它接出來。

通道有幾種值得認識的變體。有界相對於無界:有界通道容量固定,所以它滿了的時候,發送會阻塞(或暫停),直到接收端騰出空間——這是內建的背壓。無界通道永不阻塞發送端,但若接收端落後,它能無止境地增長(記憶體洩漏的風險)。端點的數量給了它們的簡稱:MPSC 是多生產者、單消費者(經典的工作匯流通道);MPMC 是多生產者、多消費者(多個工作者共同清空的共享工作佇列)。傳遞就是那個同步點——把一個值安全地交過通道,等於把它發布給接收端,於是你不需要另外一把鎖。select 是它的搭檔操作:當一個任務需要同時等待數個通道——「這些之中誰先有東西,就給我」——select 在多個通道操作上阻塞,並以第一個變就緒者繼續下去(且可以把逾時或一個取消通道納為其中一個分支)。

通道之所以重要,是因為它把「共享狀態的協調」變成一條資料流:生產者與消費者保持解耦、通道擁有交接、而有界通道免費給你流量控制。Go 的 goroutine 加通道、Rust 的 std/tokio 通道都建立在此之上。誠實的提醒:無界通道把背壓問題默默推遲到你耗盡記憶體為止;一個所有發送端都消失的通道會給出「已關閉」訊號,接收端必須處理它;而沒處理「已關閉或已取消」分支的 select 可能空轉或卡住。通道是一個協調工具,它本身並不保證正確性。

Go 的 select 等待最先就緒者:select { case v := <-ch1: use(v); case ch2 <- x: ; case <-time.After(d): timeout() }。有界通道 make(chan T, 8) 在 8 個項目未被消費時會阻塞發送端——自動背壓。

select 多工數個通道操作;有界通道在接收端落後時提供背壓。

無界通道並未移除背壓——它只是把代價藏到你耗盡記憶體為止。而通道不是無鎖的魔杖:從已關閉/空的通道接收、以及沒有就緒分支的 select,都是你必須處理的情況,否則任務會卡住。

又称
channelMPSC / MPMCselect通道多工選擇