前言
go 异步编程的便利是其能够流行的一部分原因,仅需一个 go 关键字就可以开启一个异步流程。得益于 goroutine 轻量级的设计和使用的便利性,导致 goroutine 被不正确的使用或滥用。线上由于 goroutine 使用不善导致的 panic、内存泄漏等问题屡见不鲜。
为什么做 go 协程管理
- 忘记加 defer recover 导致的 panic,go 的主协程里面加了 recover ,子协程依然需要加 recover,具体原因参考文章:go 设计与实现 - panic
- 未正确回收协程导致的内存泄漏
协程使用准则
- 使用 recover 避免 panic 导致的系统崩溃,又时候我们总是自信程序肯定没问题,但墨菲定律总是在生效,可能发生的错误一定会发生。
- 做好超时控制
- 使用 sync.WaitGroup 做好 go 生命周期管理
协程泄露案例
不正确使用阻塞 Channel
代码演示地址:执行
// basic is a program used to define a goroutine leak.
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
// Capture starting number of goroutines.
startingGs := runtime.NumGoroutine()
leak()
// Hold the program from terminating for 1 second to see
// if any goroutines created by leak terminate.
time.Sleep(time.Second)
// Capture ending number of goroutines.
endingGs := runtime.NumGoroutine()
// Report the results.
fmt.Println("========================================")
fmt.Println("Number of goroutines before:", startingGs)
fmt.Println("Number of goroutines after :", endingGs)
fmt.Println("Number of goroutines leaked:", endingGs-startingGs)
}
// leak is a buggy function. It launches a goroutine that
// blocks receiving from a channel. Nothing will ever be
// sent on that channel and the channel is never closed so
// that goroutine will be blocked forever.
func leak() {
ch := make(chan int)
go func() {
val := <-ch
fmt.Println("We received a value:", val)
}()
}
执行结果
从执行结果可以看到出现了一个协程泄漏,原因是由于 没地方消费这个 channel 从而导致协程泄漏
超时导致的协程泄漏
package main
func main() {
ch := make(chan bool)
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
defer cancel()
go leak(ctx, ch)
select {
case <-ctx.Done():
fmt.Println("done, err: ", ctx.Err())
case <-ch:
return
}
}
func leak(ctx context.Context, ch chan bool) {
time.Sleep(time.Second)
ch <- true
}
这个程序由于 leak 超时,ch 将由于没人消费永远阻塞
总结
这是一篇关于 goroutine 使用不当会引起的问题以及goroutine使用注意事项,本文旨在提高开发者的协程使用意识,使用协程的时候时刻记住 “永远不要开启你不知道什么时候结束的 goroutine”
Github:https://wang1309.github.io/
Reference
- https://www.ardanlabs.com/blog/2018/11/goroutine-leaks-the-forgotten-sender.html
- https://www.ardanlabs.com/blog/2019/04/concurrency-trap-2-incomplete-work.html
- https://www.ardanlabs.com/blog/2014/01/concurrency-goroutines-and-gomaxprocs.html