高級并發:實現 Or-Channel 模式
在實際業務中,某個 goroutine 常常依賴多個并行任務的結果。例如,同時向多臺服務器請求同一份數據,只要任意一臺返回成功即可取消其余請求。此時,需要合并多個完成(done)通道,實現“只要任一任務完成即可整體結束”的邏輯。
本文通過實現一個通用的 or 函數來演示這一模式,并展示其在動態通道數量場景下的優雅用法。

問題描述:合并多個完成通道
目標函數簽名如下:
var or func(doneChannels ...<-chan interface{}) <-chan interface{}or 接收任意數量的只讀通道,并返回一個新的只讀通道。當任一輸入通道關閉時,返回的通道立即關閉。
實現 or 函數
零或一個通道:
or = func(doneChannels ...<-chan interface{}) <-chan interface{} {
switch len(doneChannels) {
case 0:
return nil // 無通道可監聽
case 1:
return doneChannels[0] // 直接返回原通道
}
return nil
}兩個通道:
or = func(doneChannels ...<-chan interface{}) <-chan interface{} {
if len(doneChannels) < 2 {
return or(doneChannels...)
}
done := make(chan interface{})
go func() {
defer close(done)
select {
case <-doneChannels[0]:
case <-doneChannels[1]:
}
}()
return done
}任意數量通道(遞歸實現):
or = func(doneChannels ...<-chan interface{}) <-chan interface{} {
switch len(doneChannels) {
case 0:
return nil
case 1:
return doneChannels[0]
}
done := make(chan interface{})
go func() {
defer close(done)
select {
case <-doneChannels[0]:
case <-doneChannels[1]:
case <-or(doneChannels[2:]...): // 遞歸合并剩余通道
}
}()
return done
}遞歸方案可處理任意數量的通道,避免手動編寫嵌套 select 造成的可讀性下降。
使用示例
sig := func(after time.Duration) <-chan interface{} {
c := make(chan interface{})
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
<-or(
sig(2*time.Hour),
sig(5*time.Minute),
sig(1*time.Second),
sig(1*time.Hour),
sig(1*time.Minute),
)
fmt.Printf("done after %v\n", time.Since(start))輸出示例:
done after 1.002543s無論其余通道需要多長時間,or 都會在最快完成的通道關閉后立即返回控制權。
結論
or-channel 模式是 Go 并發編程中處理多源完成信號的利器。通過遞歸實現,可以在不增加復雜度的前提下支持動態數量的通道。這一技巧適用于:
- 聚合超時或取消信號;
- 構建彈性系統,快速響應最先完成的任務;
- 合并多個結果通道的完成事件;
- 合理運用該模式,可顯著提升并發代碼的可讀性與健壯性。































