1. 最好在主 goroutine 裏定義channel並準備初始數據,再根據需要發出子goroutine 實現具體處理邏輯,各子goroutine可以從 channel 接收數據和(或)往 channel 發送數據。
2. 如果不是特殊需要,不要定義無緩衝的 channal,也不要在主goroutine裏從channel裏接收或往其發送數據。
3. 原則上,由發送方主動關閉channel, 接收方通過影子變量得知channel是否關閉。
4. 儘量使用無阻塞的的select 輪詢,也就是select 語句塊包含一個default 分支。無阻塞的select只是說select語句塊不會被阻塞,任一case從 channel 接收或發送數據都有可能阻塞,任一時刻當所有case分支阻塞時,走default分支。爲了多次輪詢若干個 channel, 可以使用 for loop 循環調用無阻塞的 select。
5. 如果無阻塞的select語句塊作用在 unbuffered channel 上,會由於channel一直阻塞,goroutine 會直接從 default 分支走掉。建議使用 buffered channel 並且 capacity 足夠大。
6. 無阻塞的 select 最好有超時實現。如有需要,可調用time包的time.NewTimer(n * time.Second).C 返回一個絕對到期時間的通道,通道里包含的是絕對到期時間值。
7. 如果需要類似回調功能,可以定義結構體A,其嵌套一個 channel 叫 resultChan,請求方 goroutineA 發送A到某個 channel 叫 messageChan,處理方 goroutineB 從 messageChan 裏接收 A,處理好後將結果發送到 resultChan,goroutineA 從 resultChan 接收最終結果然後處理,完成回調。
比較常用的無阻塞讀取數據的代碼:
aChannel := make(chan int, 3)
go func(){
DONE:
for { // for循環可以不斷地從aChannel裏讀取數據,知道超時
select {
case e, ok := <- aChannel: // 影子變量判斷通道是否關閉了
if ok {
fmt.Println("receive a value", e)
// handleReceivedValue(e)
} else {
fmt.Println("Channel had been closed.")
break DONE // 退出整個for 循環
}
case <- time.NewTimer(5 * time.Second).C: // 調用time包裏的官方超時實現,代碼非常簡潔
fmt.Println("Timeout")
break DONE
default: // default分支保證select語句不被阻塞
fmt.Println("no case fired, go default")
}
}
}()