main函数返回程序立即退出,所有goroutine被强制终止;必须用sync.WaitGroup等显式同步机制确保goroutine完成,wg.Add(1)需在go前调用,defer wg.Done()放入口,wg.Wait()在main中调用。

主函数返回,程序立刻退出,所有未完成的 goroutine 被强制中止 —— 这不是 bug,是 Go 的确定性行为。你不能靠“它应该跑完”来写代码,必须显式管理。
main 函数结束即进程死亡,goroutine 不会自动等待
这是绝大多数 goroutine 意外消失、日志截断、HTTP 服务秒退的根本原因。Go 运行时不做任何存活引用追踪,main 函数一返回,整个进程被操作系统回收,所有非主 goroutine 瞬间终止,defer 不执行、channel 不关闭、文件不 flush。
- 典型错误:
go http.ListenAndServe(":8080", nil)后直接return或函数末尾无阻塞,服务启动后立即退出 - 验证方式:在 goroutine 里加
time.Sleep(5 * time.Second),再在main最后打一行fmt.Println("done")—— 你会看到 "done" 输出,但 sleep 永远不会完成 - 关键认知:goroutine 没有“后台线程”语义,它和 main 是平级的调度单元,不是子进程
sync.WaitGroup 是最稳妥的“等完再退”方案
当你明确知道要等几个 goroutine(比如启动 3 个 worker、1 个 monitor),sync.WaitGroup 是零歧义、低心智负担的选择。它只做一件事:计数。
-
wg.Add(1)必须在go语句之前调用,绝不能放在 goroutine 内部 —— 否则可能因竞态漏加,或Wait()已返回而新 goroutine 才开始 -
defer wg.Done()放在 goroutine 入口处,确保无论从哪个分支 return 都能减计数 -
wg.Wait()必须在 main goroutine 中调用,且不能放在 goroutine 启动循环内部(否则会阻塞后续启动) - 示例片段:
var wg sync.WaitGroup<br>for i := 0; i < 3; i++ {<br> wg.Add(1)<br> go func(id int) {<br> defer wg.Done()<br> // 实际工作逻辑<br> }(i)<br>}<br>wg.Wait() // 所有 goroutine 完成后才继续
长期运行的 goroutine 必须接收 context.Context
如果 goroutine 要监听 channel、轮询状态、处理 WebSocket 连接或做定时任务,WaitGroup 就失效了 —— 它无法提前叫停。这时必须用 context.Context 提供取消信号。
立即学习“go语言免费学习笔记(深入)”;
- 所有长期 goroutine 函数签名应带
ctx context.Context参数,例如:func serveHTTP(ctx context.Context, addr string) - 在
select中必须包含case 分支,并在该分支中 return 或 cleanup - 派生 context 用
context.WithCancel(手动触发)、context.WithTimeout(防卡死)、context.WithDeadline(精确截止) - 常见坑:
time.After创建的 timer 不 stop 会导致资源滞留;应优先用time.NewTimer+defer t.Stop()
channel 关闭不当会引发死锁或泄漏
goroutine 阻塞在 上却没人关 channel,是典型的 “all goroutines are asleep - deadlock!” 根源。关闭时机和主体必须清晰。
- 发送方负责关闭 channel —— 接收方永远不应 close 一个只读 channel(编译报错)
- 关闭前确保所有发送已结束,且没有其他 goroutine 试图再 send(panic: send on closed channel)
- 接收方要用
val, ok := 检查 <code>ok,避免无限阻塞;或配合range ch(自动在 channel 关闭后退出) - 多个接收者?别关 channel —— 改用
context.Context通知退出更安全
最易被忽略的一点:goroutine 生命周期不是“启动了就完事”,而是“谁负责判断它该停、怎么通知它停、停之前要做什么”。没有统一模板,但每条 goroutine 都得有明确的退出路径 —— 否则它要么早死(main 退出),要么长生(泄漏)。


















