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


你可能感兴趣的:(Golang)