goroutine使用最佳實踐

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")
		}
	}
}()


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章