Go channel系列:html
雙層通道的解釋見Go的雙層通道併發
如下是一個雙層通道的使用示例。注意下面的示例中使用了"信號通道"(Signal channel),但這裏的信號通道是多餘的,僅僅只是爲了介紹。函數
信號通道不用來傳遞數據,而是用來傳遞消息,用來產生可讀、可寫的事件,以便讓select選中某個分支。產生消息事件的方式有多種,好比直接關閉通道、發送false/true布爾值等等。code
package main import ( "fmt" "time" ) func main() { // 定義雙層通道cc cc := make(chan chan int) times := 5 for i := 1; i < times+1; i++ { // 定義信號通道f f := make(chan bool) // 每次循環都在雙層通道cc中生成內層通道c // 並經過信號通道f來終止f1() go f1(cc, f) // 從雙層通道cc中取出內層通道ch // 並向ch通道發送數據 ch := <-cc ch <- i // 從ch中取出數據 for sum := range ch { fmt.Printf("Sum(%d)=%d\n", i, sum) } // 每一個循環睡眠一秒鐘 time.Sleep(time.Second) // 每次循環都關閉信號通道f close(f) } } // 雙層通道cc用來生成內層通道c // 並使用信號通道f來終止函數f1() func f1(cc chan chan int, f chan bool) { c := make(chan int) cc <- c defer close(c) sum := 0 select { // 從內層通道中取出數據,計算和,而後發回內層通道 case x := <-c: for i := 0; i <= x; i++ { sum = sum + i } // goroutine將阻塞在此,直到數據被讀走 c <- sum // 信號通道f可讀時,結束f1()的運行 // 但由於select沒有在for中,該case分支用不上 case <-f: return } }
上面的示例中,函數f1()兩個參數,一個是雙層通道cc,一個是信號通道f。f1()中首先生成了一個通道c,併發送給了雙層通道cc,使得main()中能夠從cc中取得這個內層通道c,並向其發送數據。htm
回到f1()中,select最初會被阻塞,由於內層通道c和信號通道f都沒有數據可讀。因爲main()能夠取得內層通道c,並向其發送數據,使得f1()中的select第一個case分支被選中,該分支會計算髮送的整數以前的總和,並將計算結果從新發送給內層通道c,讓main()能夠取得這個計算結果。blog
上面的示例中有幾個細節須要注意:事件
因此,當在select中有發送操做的時候,極可能會出現死鎖現象。這時,要麼爲select加上default,要麼爲select加上超時時間,要麼select不要放在for循環中。get