ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Goroutine 泄漏的 3 个高频坑,第 2 个生产环境天天见

Goroutine 泄漏的 3 个高频坑,第 2 个生产环境天天见 Goroutine 泄漏的 3 个高频坑第 2 个生产环境天天见半夜三点告警 CPU 100%排查半天发现是 Goroutine 只增不减。这种“静默泄漏”在生产环境太常见了很多老手也在 WaitGroup 和 Channel 上栽过跟头。今天不聊虚的直接上代码复盘三个最频发的并发坑帮你避开 90% 的线上故障。坑一WaitGroup 加锁位置错误很多新人习惯在 goroutine 内部调用wg.Add(1)这是典型的竞态条件。主 goroutine 可能在子 goroutine 执行Add之前就调用了Wait导致计数器永远为 0程序直接退出子任务丢失。func wrongWaitGroup() { var wg sync.WaitGroup for i : 0; i 3; i { go func() { wg.Add(1) // 错误可能在 Wait 之后执行 defer wg.Done() fmt.Println(Task) }() } wg.Wait() }输出/效果程序可能直接结束没有任何输出或者 panic: sync: WaitGroup is reused before negative counter。修正必须在启动 goroutine 之前在主线程中调用wg.Add(1)。坑二Channel 无缓冲且无人接收这是生产环境天天见的死锁。创建一个无缓冲 channel发送数据时会阻塞直到有接收者。如果所有 goroutine 都在等待发送而没人接收整个程序就会死锁。func channelDeadlock() { ch : make(chan int) go func() { ch - 1 // 阻塞在这里因为没人接收 }() // 主线程也没接收直接结束或死锁 time.Sleep(100 * time.Millisecond) }输出/效果如果主线程不退出会报 fatal error: all goroutines are asleep - deadlock!。修正确保有对应的接收逻辑或使用带缓冲 channelmake(chan int, 1)但要注意缓冲满了依然会阻塞。坑三忽略 Context 取消信号长驻 goroutine 如果不监听ctx.Done()即使外部调用了cancel()子 goroutine 依然在跑导致资源泄漏。这是微服务重启慢的元凶之一。func leakContext() { ctx, cancel : context.WithCancel(context.Background()) go func() { for { // 缺少 select -ctx.Done() fmt.Println(Working...) time.Sleep(1 * time.Second) } }() cancel() // 调用取消但 goroutine 停不下来 time.Sleep(2 * time.Second) }输出/效果控制台持续输出 Working...即使 cancel 已调用goroutine 无法优雅退出。修正在循环内使用select监听ctx.Done()收到信号立即return。避坑检查清单1.WaitGroup 规范Add必须在go关键字之前且主线程调用。2.Channel 闭环发送方关闭 channel 或确保接收方永远在线避免死锁。3.Context 传递所有长驻 goroutine 必须传入 context 并监听 Done 通道。4.资源清理使用defer确保锁、连接等资源在异常退出时释放。并发写起来爽排查起来火葬场。上面这三个例子你在生产环境踩过几个
返回列表