有時你會與來自系統不同部分的Channel(通道)交互。與pipeline(管道)不同的是,當你使用的代碼通過done通道取消操作時,你無法對通道的行為方式做出判斷。也就是說,你不知道正在執行讀取操作的goroutine現在是什麽 狀態。出於這個原因,正如我們在“防止Goroutine泄漏”中所闡述的那樣,需要用select語句來封裝我們的讀取操作和done通道。可以簡單的寫成這樣:
loop:
for {
select {
case <-done:
break loop
case maybeVal, ok := <-myChan:
if ok == false {
return // or maybe break from for
}
// Do something with val
}
}
這樣做可以快速退出嵌套循環。繼續使用goroutines編寫更清晰的並發代碼,而不是過早優化的主題,我們可以用一個goroutine來解決這個問題。
我們封裝了細節,以便其他人調用更方便:
func main() {
orDone := func(done, c <-chan interface{}) <-chan interface{} {
valStream := make(chan interface{})
go func() {
defer close(valStream)
for {
select {
case <-done:
return
case v, ok := <-c:
if ok == false {
return
}
select {
case valStream <- v:
case <-done:
}
}
}
}()
return valStream
}
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
}
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)
}
}