最近在開發過程中遇到了幾個goroutine通訊的問題,我以爲這幾個問題很是具備表明性,所以拿出來和你們分享一下。golang
開發過程中有遇到這樣的一種狀況,須要檢查channel是否關閉,若是關閉則不進行相應操做,不然會panic等現象。在golang的select語法當中,default分支能夠解決上述問題,請看以下例子:併發
closechan := make(chan int,0) dchan := make(chan int,0) select{ case <- closechan: fmt.Println("channel already closed.") return default: fmt.Println("channel not closed, do your things") dchan <- 1 //1 } go func(){ for{ select{ case data := <- dchan: //2 err := doSomeThing(data) //3 if err != nil /* some thing wrong*/ { //4 close(closechan) //5 } } } }
上述的方式能夠在處理dchan
的數據的時候處理異常並再也不接受來自dchan的數據。code
可是咱們須要考慮一種狀況,若是在doSomeThing(data)
的過程中,出現異常(4)
,那麼不容許往dchan
發送數據,並將closechan
關閉。在(5)
關閉closechan
以後就不會再進入(1)
的default流程了。開發
但是這還有問題,那就是若是doSomeThing(data)
處理的過程中,新來了一個數據進入到了dchan
,那麼將會在(1)
和(2)
處阻塞,當doSomeThing(data)
處理完以後,還會多處理一次異常狀況,也就是說在(5)
處將會close(closechan)
兩次,這樣會致使panic: close of closed channel
,因此咱們須要在(5)
再寫一個相應的default
處理邏輯:it
go func(){ for{ select{ case data := <- dchan: //2 err := doSomeThing(data) //3 if err != nil /* some thing wrong*/ { //4 select{ case <-closechan: //do nothing return default: close(closechan) //5 } } } } }
當咱們在使用bufferd-channel的時候,咱們可能須要檢查當前的channel是否已經滿了,由於咱們可能不但願此時goroutine阻塞,因此能夠採用以下的方式進行處理:class
cc := make(chan int, 1) cc <- data1 select { case cc <- data2: fmt.Println("data already en-channel") default: fmt.Println("cc was full") }
在研究併發map的時候,會考慮到一種shard-map的實現方式,在讀取map中的值的時候,須要經過多個小map中獲取完整的map值,能夠利用channel實現fan-in:sed
func fanIn(chans []chan int, out chan int) { wg := sync.WaitGroup{} wg.Add(len(chans)) for _, ch := range chans { go func(ch chan int) { for t := range ch { out <- t } wg.Done() }(ch) } wg.Wait() close(out) }