主程序提前退出导致goroutine被强制终止,正确做法是用sync.waitgroup显式等待:add()须在go前调用,done()应defer在goroutine内,wait()阻塞main直至计数归零。

Go 语言里启动并发任务,go 关键字就够了,但直接这么写大概率会丢数据、程序提前退出、或 panic——因为 Goroutine 是“火点就跑”,没人等它。
goroutine 启动后主函数立刻退出怎么办
这是新手最常遇到的问题:加了 go,却看不到输出,或者只看到部分结果。根本原因是 main 函数执行完就退出,所有 Goroutine 被强制终止。
- 不能靠
time.Sleep硬等,它不可靠:睡短了没跑完,睡长了浪费资源,且无法适配不同逻辑耗时 - 正确做法是用
sync.WaitGroup主动等待所有 Goroutine 完成 -
WaitGroup.Add()必须在 Goroutine 启动前调用,否则可能漏计数 -
defer wg.Done()要放在 Goroutine 内部最开头,确保无论是否 panic 都能触发减计数
WaitGroup 使用中容易漏掉的三个细节
sync.WaitGroup 看似简单,但几个关键点错一个就会导致死锁或 panic:
-
WaitGroup的零值可用,但不能复制:传参时必须传指针,或在函数内声明为局部变量(避免结构体字段被浅拷贝) -
Add()参数为负数会 panic,比如误写wg.Add(-1) -
Done()调用次数超过Add()次数也会 panic,常见于循环中重复 defer 或多处调用
goroutine 泄漏:为什么程序内存越跑越高
看似正常运行的并发代码,过一段时间发现内存持续上涨,大概率是 Goroutine 没退出——即“goroutine 泄漏”。典型场景有:
- 向无缓冲
chan发送数据,但没人接收,发送方 Goroutine 永久阻塞 - 从空
chan接收数据,但没人发送,接收方永久阻塞 - 用
select等待 channel 时没加default或超时,导致 Goroutine 卡住 - 忘记关闭 channel,下游一直等待新数据
goroutine 数量不是越多越好
虽然 Go 支持百万级 Goroutine,但盲目堆数量反而损害性能:
- 每个 Goroutine 默认栈约 2KB,十万 goroutine 就占 200MB 内存,实际还更多(调度元数据、GC 压力)
- 过度并发会加剧调度器负担,CPU 缓存失效增多,吞吐不升反降
- I/O 密集型任务(如 HTTP 请求)适合高并发;计算密集型任务应限制并发数,通常不超过
runtime.NumCPU() - 用
worker pool模式控制并发上限,比无节制go f()更健壮
真正难的不是启动 Goroutine,而是让它安全结束、可控调度、不泄漏也不过载——这些细节藏在 WaitGroup 的调用顺序、chan 的缓冲策略、以及你对阻塞点的预判里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











