Posted on May 13, 2019
| 3 minutes
| 568 words
| appleboy
大家在初學 Go 語言時,肯定很少用到 Go Channel,也不太確定使用的時機點,其實在官方 Blog 有提供一篇不錯的文章『Go Concurrency Patterns: Pipelines and cancellation』,相信大家剛跨入學習新語言時,不會馬上看 Go Channel,底下我來用一個簡單的例子來說明如何使用 Go Channel,使用情境非常簡單,就是假設今天要同時處理 20 個背景工作,一定想到要使用 Goroutines,但是又想要收到這 20 個 JOB 處理的結果,並顯示在畫面上,如果其中一個 Job 失敗,就跳出 main 函式,當然又會希望這 20 個 JOB 預期在一分鐘內執行結束,如果超過一分鐘,也是一樣跳出 main 函式。針對這個問題,我們可以整理需要三個 Channel + 一個 Timeout 機制。
Loop:
for {
select {
case val := <-outChan:
fmt.Println("finished:", val)
case err := <-errChan:
fmt.Println("error:", err)
break Loop
case <-finishChan:
break Loop
}
}
這邊我們看到需要加上 Loop 自定義 Tag,來達到 break for 迴圈,而不是 break select 函式。但是有沒有發現程式碼會一直卡在 wg.Wait(),不會進入到 for 迴圈內,這時候就必須將 wg.Wait() 丟到背景。
for i := 0; i < 20; i++ {
gofunc(outChan chan<- int, errChan chan<- error, val int, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond)
fmt.Println("finished job id:", val)
outChan <- val
if val == 11 {
errChan <- errors.New("error in 60")
}
}(outChan, errChan, i, &wg)
}
宣告 chan<- int 代表在 go func 只能將訊息丟到通道內,而不能讀取通道。
心得
希望透過上述簡單的例子,讓大家初學 Go 的時候有個基礎的理解。用法其實不難,但是請參考專案內容特性來決定如何使用 Channel,最後附上完整的程式碼: