goroutine泄露

目录

一.怎么避免goroutine泄露问题?

二.排查goroutine泄露

三.参考资源:


一.怎么避免goroutine泄露问题?

func main() {
	//协程泄露的问题
	//产生泄露问题的demo如下

	//一.goroutine+channel结合

	1.发送不接受(接受不完全)
	//for i := 0; i < 4; i++ {
	//	queryAll()
	//	fmt.Printf("接受的数:%d \n,", runtime.NumGoroutine())
	//	//最后输出的 goroutines 数量是在不断增加的,每次多 2 个。也就是每调用一次,都会泄露 Goroutine。
	//	//NumGoroutine 获取当前运行中的 goroutine 数量粗略估计。如果 goroutine 随着时间增加,数量在不断上升,而基本没有下降,基本可以确定存在泄露。
	//	//这里的泄露在于 queryAll每次都会往channel发送三个数,但接受端并没有接受完全(仅接受了一个ch),所以产生泄露
	//}

	2.接受不发送
	//defer func() {
	//	fmt.Printf("已存在的goroutines:%d\n", runtime.NumGoroutine())
	//}()
	//var ch chan struct{}
	//func() {
	//	ch <- struct{}{}
	//}()
	//time.Sleep(time.Second)
	这个就是channel接受了值,但并没有发送,也会产生阻塞
	在业务场景中,许多业务逻辑里,基本只要有一个channel读写出现问题都会发生阻塞

	3.nil channel
	//defer func() {
	//	fmt.Printf("已存在的goroutines:%d\n", runtime.NumGoroutine()) 
      // 获取当前运行中的 goroutine 数量粗略估计,
	//
	//}()
	//var ch chan int
	//func() {
	//	ch <- 1
	//}()
	//fmt.Println(<-ch)
	//time.Sleep(time.Second)
	这个demo在于没有进行初始化channel,所以无论是读或者写这个通道都是不可用的
	3.1正确初始化方式如下
	//ch := make(chan int)
	//go func() {
	//	ch <- 0
	//}()
	//go func() {
	//	ch <- 1
	//}()
	//fmt.Println(<-ch)
	//time.Sleep(time.Second)

	二.奇怪的慢等待
	//
	//for {
	//	go func() {
	//		_, err := http.Get("https://www.baidu.com/")
	//		if err != nil {
	//			fmt.Printf("http.Get err: %v\n", err)
	//		}
	//		// do something...
	//
	//	}()
	//
	//	time.Sleep(time.Second * 1)
	//	fmt.Println("goroutines: ", runtime.NumGoroutine())
	//}
	这个奇怪的慢等待demo 展示了Go语言中比肩常见的经典场景,也就是一般应用程序访问第三方接口时,而第三方接口迟迟不返回响应结果,刚好Go语言中的http.Client没有设置超时时间,因此会导致阻塞导致goroutine暴涨,导致不断泄露,最后导致事故。

	三.互斥锁忘记解锁
	//total := 0
	//defer func() {
	//	time.Sleep(time.Second)
	//	fmt.Printf("total:%d\n", total)
	//	fmt.Printf("当前正在运行的goroutine数量:%d\n", runtime.NumGoroutine())
	//}()
	//var mutex sync.Mutex
	//for i := 0; i < 10; i++ {
	//	func() {
	//		mutex.Lock()
	//		defer mutex.Unlock() //在使用sync.Mutex互斥锁时时这里Unlock需要注意
	//		total++
	//
	//	}()
	//}
	在这个例子中第一个互斥锁加锁了,但没有进行Unlock,因此导致后面的所有 sync.Mutex 想加锁,却因未释放又都阻塞住了,直接goroutine泄露导致死锁现象,正确应该在mutex.lock后加一个mutex.Unlock

	//四.同步锁使用不当
	defer func() {
		fmt.Println("goroutines: ", runtime.NumGoroutine())
	}()

	go handle(3)
	time.Sleep(time.Second)

}

func queryAll() int {
	ch := make(chan int)
	for i := 0; i < 3; i++ {
		go func() {
			ch <- queryOne()
		}()
	}
	return <-ch
}


func queryOne() int {
	n := rand.Intn(10)                              //返回一个半开区间的随机整数,当n<=0,会发生panic
	time.Sleep(time.Duration(n) * time.Millisecond) //一个持续时间以int64纳秒计数表示两个瞬间之间的时间。这种表示方法将最大的可表示的持续时间限制在大约290年。
	return n
}

func handle(v int) {
	错误用例
	//var wg sync.WaitGroup
	//wg.Add(5)
	//for i := 0; i < v; i++ {
	//	fmt.Println("脑子进煎鱼了")
	//	wg.Done()
	//}
	//wg.Wait()
	//在这个例子中,我们调用了同步编排 sync.WaitGroup,模拟了一遍我们会从外部传入循环遍历的控制变量。
	//但由于 wg.Add 的数量与 wg.Done 数量并不匹配,因此在调用 wg.Wait 方法后一直阻塞等待。

	var wg sync.WaitGroup
	for i := 0; i < v; i++ {
		wg.Add(1)
		defer wg.Done()
		fmt.Println("脑子进煎鱼了")
	}
	wg.Wait()
}

二.排查goroutine泄露

主要用`runtime.NumGoroutine`或者`pprof`工具。`pprof`会返回所有带有堆栈跟踪的`goroutine`列表。

三.参考资源:

煎鱼------[跟读者聊 Goroutine 泄露的 N 种方法,真刺激!](https://blog.csdn.net/EDDYCJY/article/details/115535237)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值