Go Code Review建议以及自查表

管理Goroutine的生命周期

Rule 1:永远不要开始一个你无法确他何时退出的协程
一个无法退出的goroutine可能是阻塞在某些channel上了,对于这种永久阻塞,GC并不会将goroutine停止,并释放内存,这将导致goroutine leak 内存泄漏。

Goroutines can leak by blocking on channel sends or receives: the garbage collector will not terminate a goroutine even if the channels it is blocked on are unreachable.
https://github.com/golang/go/wiki/CodeReviewComments#goroutine-lifetimes

即使goroutine并没有阻塞在channel,而是在期望他已关闭的场景下继续运行。这将可能导致很难诊断的错误,可能会引起panic或者是data races,也可能会导致内存飙升等一系列头疼问题。

如何管理goroutine声明周期?可以尝试一下的tips:

  1. 尽量让你的goroutine代码简单清晰,简单到一眼就能看穿其生命周期。
  2. 对于每个goroutine方法,都应将其生命周期注释清楚,说明其退出的条件。
  3. 在父级goroutine中使用context来管理子goroutine的声明周期。
  4. 使用专门的channel通知goroutine结束

channel通知退出模式

以下用我最喜欢的channel通知退出模式来举例说明:

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

type add struct {
	done chan struct{} //退出信号
	in   chan int
	out  chan int
	wg   sync.WaitGroup
}

//每一秒往in里添加一个随机数,当close(a.done)时退出
func (a *add) input() {
	defer a.wg.Done()
	for {
		select {
		case <-a.done:
			return
		case <-time.After(1 * time.Second):
			a.in <- rand.Intn(100)
		}
	}

}

//从in里取出内容,并+1,写入out,当close(a.done)时退出
func (a *add) process() {
	defer a.wg.Done()
	for {
		select {
		case <-a.done:
			return
		case i := <-a.in:
			i++
			a.out <- i
		}
	}
}

//打印out里的值,当close(a.done)时退出
func (a *add) output() {
	defer a.wg.Done()
	for {
		select {
		case <-a.done:
			return
		case o := <-a.out:
			fmt.Println(o)
		}
	}
}
func main() {
	a := &add{done: make(chan struct{}), in: make(chan int, 5), out: make(chan int, 5)}
	a.wg.Add(3)
	go a.input()
	go a.process()
	go a.output()
	<-time.After(5 * time.Second) //5秒后结束
	close(a.done)
	a.wg.Wait() //等待goroutine全部结束
	fmt.Println("结束")
}

在上例的程序中,我们启用了3个协程,设置定时器在5秒后全部结束,通过close的广播机制,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值