最近公司工做有點多,Golang的select進階就這樣被拖沓啦,今天堅持把時間擠一擠,把吹的牛皮補上。golang
前一篇文章《Golang併發模型:輕鬆入門select》介紹了select的做用和它的基本用法,此次介紹它的3個進階特性。後端
nil
的通道永遠阻塞for-select
select{}
阻塞nil
的通道永遠阻塞當case
上讀一個通道時,若是這個通道是nil
,則該case
永遠阻塞。這個功能有1個妙用,select
一般處理的是多個通道,當某個讀通道關閉了,但不想select
再繼續關注此case
,繼續處理其餘case
,把該通道設置爲nil
便可。
下面是一個合併程序等待兩個輸入通道都關閉後才退出的例子,就使用了這個特性。緩存
func combine(inCh1, inCh2 <-chan int) <-chan int { // 輸出通道 out := make(chan int) // 啓動協程合併數據 go func() { defer close(out) for { select { case x, open := <-inCh1: if !open { inCh1 = nil continue } out<-x case x, open := <-inCh2: if !open { inCh2 = nil continue } out<-x } // 當ch1和ch2都關閉是才退出 if inCh1 == nil && inCh2 == nil { break } } }() return out }
break
在select
內的並不能跳出for-select
循環。看下面的例子,consume
函數從通道inCh
不停讀數據,期待在inCh
關閉後退出for-select
循環,但結果是永遠沒有退出。bash
func consume(inCh <-chan int) { i := 0 for { fmt.Printf("for: %d\n", i) select { case x, open := <-inCh: if !open { break } fmt.Printf("read: %d\n", x) } i++ } fmt.Println("combine-routine exit") }
運行結果:併發
➜ go run x.go for: 0 read: 0 for: 1 read: 1 for: 2 read: 2 for: 3 gen exit for: 4 for: 5 for: 6 for: 7 for: 8 ... // never stop
既然break
不能跳出for-select
,那怎麼辦呢?給你3個錦囊:less
case
內,使用return
,若是有結尾工做,嘗試交給defer
。select
外for
內使用break
挑出循環,如combine
函數。goto
。select{}
永遠阻塞select{}
的效果等價於建立了1個通道,直接從通道讀數據:函數
ch := make(chan int) <-ch
可是,這個寫起來多麻煩啊!沒select{}
簡潔啊。
可是,永遠阻塞能有什麼用呢!?
當你開發一個併發程序的時候,main
函數千萬不能在子協程幹完活前退出啊,否則全部的協程都被迫退出了,還怎麼提供服務呢?
好比,寫了個Web服務程序,端口監聽、後端處理等等都在子協程跑起來了,main
函數這時候能退出嗎?spa
最後,介紹下我經常使用的select
場景:code
select
本色:多通道處理
- 若是這篇文章對你有幫助,請點個贊/喜歡,鼓勵我持續分享,感謝。
- 個人文章列表,點此可查看
- 若是喜歡本文,隨意轉載,但請保留此原文連接。