在某些情況下,你可能會發現自己想要使用一系列通道的值:
```
<-chan <-chan interface{}
```
這與將某個通道的數據切片合并到一個通道中稍有不同,這種調用方式意味著一系列通道有序的寫入操作。這與管道的單個“階段”類似,其生命周期是間歇性的。按“訪問范圍約束”章節所提到的,通道由寫入它們的goroutine所擁有,每當在新的goroutine中啟動一個管道的“階段”時,就會創建一個新的通道——這意味著我們會得到一個通道隊列。我們會在第五章“Goroutines異常行為修復”中詳細討論。
作為消費者,代碼可能不關心其值來自于一系列通道的事實。在這種情況下,處理一系列通道中的單個通道可能很麻煩。如果我們定義一個函數,可以將一系列通道拆解為一個簡單的通道——我們成為通道橋接(bridge-channle),這使得消費者更容易關注手頭的問題:
```
bridge := func(done <-chan interface{}, chanStream <-chan <-chan interface{}) <-chan interface{} {
valStream := make(chan interface{}) // 1
go func() {
defer close(valStream)
for { // 2
var stream <-chan interface{}
select {
case maybeStream, ok := <-chanStream:
if ok == false {
return
}
stream = maybeStream
case <-done:
return
}
for val := range orDone(done, stream) { // 3
select {
case valStream <- val:
case <-done:
}
}
}
}()
return valStream
}
```
1. 這個通道會返回所有傳入bridge的通道。
2. 該循環負責從chanStream中提取通道并將其提供給嵌套循環以供使用。
3. 該循環負責讀取已經給出的通道的值,并將這些值重復到valStream中。當前正在循環的流關閉時,我們跳出執行從該通道讀取的循環,并繼續下一次循環來選擇要讀取的通道。 這為我們提供了一個不間斷的流。
這段代碼非常直白。接下來我們來使用它。下面這個例子創建了10個通道,每個通道都寫入一個元素,并將這些通道傳遞給bridge:
```
genVals := func() <-chan <-chan interface{} {
chanStream := make(chan (<-chan interface{}))
go func() {
defer close(chanStream)
for i := 0; i < 10; i++ {
stream := make(chan interface{}, 1)
stream <- i
close(stream)
chanStream <- stream
}
}()
return chanStream
}
for v := range bridge(nil, genVals()) {
fmt.Printf("%v ", v)
}
```
這會輸出:
```
0 1 2 3 4 5 6 7 8 9
```
通過使用bridge,我們可以專注于解構之外的邏輯,而無需去關心大量的通道處理問題。
* * * * *
學識淺薄,錯誤在所難免。我是長風,歡迎來Golang中國的群(211938256)就本書提出修改意見。
- 前序
- 誰適合讀這本書
- 章節導讀
- 在線資源
- 第一章 并發編程介紹
- 摩爾定律,可伸縮網絡和我們所處的困境
- 為什么并發編程如此困難
- 數據競爭
- 原子性
- 內存訪問同步
- 死鎖,活鎖和鎖的饑餓問題
- 死鎖
- 活鎖
- 饑餓
- 并發安全性
- 優雅的面對復雜性
- 第二章 代碼建模:序列化交互處理
- 并發與并行
- 什么是CSP
- CSP在Go中的衍生物
- Go的并發哲學
- 第三章 Go的并發構建模塊
- Goroutines
- sync包
- WaitGroup
- Mutex和RWMutex
- Cond
- Once
- Pool
- Channels
- select語句
- GOMAXPROCS
- 結論
- 第四章 Go的并發編程范式
- 訪問范圍約束
- fo-select循環
- 防止Goroutine泄漏
- or-channel
- 錯誤處理
- 管道
- 構建管道的最佳實踐
- 便利的生成器
- 扇入扇出
- or-done-channel
- tee-channel
- bridge-channel
- 隊列
- context包
- 小結
- 第五章 可伸縮并發設計
- 錯誤傳遞
- 超時和取消
- 心跳
- 請求并發復制處理
- 速率限制
- Goroutines異常行為修復
- 本章小結
- 第六章 Goroutines和Go運行時
- 任務調度