最近在開發過程當中遇到了幾個goroutine通訊的問題,我覺得這幾個問題非常具有代表性,因此拿出來和大家分享一下。
檢查channel是否關閉
開發過程當中有遇到這樣的一種情況,需要檢查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的資料。
但是我們需要考慮一種情況,如果在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
處理邏輯:
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 } } } }}
檢查buffered-channel是否已滿
當我們在使用bufferd-channel的時候,我們可能需要檢查當前的channel是否已經滿了,因為我們可能不希望此時goroutine阻塞,所以可以採用如下的方式進行處理:
cc := make(chan int, 1)cc <- data1select { case cc <- data2: fmt.Println("data already en-channel") default: fmt.Println("cc was full")}
fan-in
在研究並發map的時候,會考慮到一種shard-map的實現方式,在讀取map中的值的時候,需要通過多個小map中擷取完整的map值,可以利用channel實現fan-in:
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)}