go程序在main函数执行完毕后立即终止,不会等待其他goroutine完成;即使goroutine已启动并阻塞在channel接收上,若main提前退出,其打印语句将永远不会执行。

goroutine 启动后主程序立刻退出,为什么?
因为 Go 程序默认只等待 main 函数结束,不感知其他 goroutine 是否还在运行。哪怕你写了 go fmt.Println("hello"),只要 main 执行完,整个进程就终止了,那行打印根本来不及输出。
常见错误现象:go run main.go 运行后无任何输出,或只输出部分日志;加了 time.Sleep 才“看起来正常”,但这是伪解法。
- 正确做法是用
sync.WaitGroup显式等待:在启动 goroutine 前调用wg.Add(1),goroutine 结束前调用wg.Done(),主函数末尾阻塞wg.Wait() - 避免用
time.Sleep控制等待时间——它不可靠、不精确,且掩盖了同步逻辑缺失的本质问题 - 如果 goroutine 本身有超时或取消需求,应配合
context.Context使用,而不是靠 sleep 或死等
channel 发送/接收卡死(deadlock)怎么定位?
最典型的 fatal error: all goroutines are asleep - deadlock 错误,本质是某条 goroutine 在 channel 上无限等待:要么没人接收,要么没人发送。
使用场景中容易出错的点:
- 向未缓冲的 channel
ch := make(chan int)发送数据,但没有 goroutine 同时在另一端接收 → 立即阻塞 - 用
for range ch遍历 channel,但 sender 没调用close(ch)→ range 永远等不到关闭信号 - 多个 goroutine 共用一个 channel,但漏掉某条路径的 send 或 recv,导致某一方永远挂起
调试建议:先确认 channel 是否已 close;再检查所有 send/recv 是否成对出现;必要时用 select 加 default 分支做非阻塞探测。
sync.Mutex 和 channel,到底该用哪个做并发控制?
不是“哪个更好”,而是“解决什么问题”。选错会导致代码难维护、性能下降,甚至隐藏竞态。
- 用
sync.Mutex:保护共享内存状态,比如修改全局计数器、更新 map、写日志缓冲区。注意必须成对出现mu.Lock()/mu.Unlock(),且不能在 defer 里 unlock 跨函数边界 - 用
channel:协调任务流程、传递结果、实现生产者-消费者模型。例如:把一批任务发到jobs chan Task,多个 worker goroutine 从里面取任务处理并回传结果 - 别混用:不要用 channel 去保护一个变量读写(太重),也别用 Mutex 去替代 goroutine 间的数据流转(破坏 Go 的 CSP 思想)
高并发服务上线前,哪些 goroutine 行为必须检查?
线上服务崩得悄无声息,往往不是逻辑错,而是 goroutine 泄漏或失控。几个关键检查点:
- 所有
go func() { ... }()是否有明确退出条件?无限循环没 break、没 context.Done() 监听,就会永久存活 - HTTP handler 里启的 goroutine,是否绑定请求生命周期?没用
req.Context()做 cancel,请求中断后 goroutine 还在跑 - channel 是否有缓冲?无缓冲 channel 在高并发下极易成为瓶颈;缓冲大小设太小会丢数据,太大则内存暴涨
- 是否依赖
time.After或time.Tick启动定时 goroutine?它们底层创建新 timer,不用时要显式 stop,否则泄漏
真正难的不是写并发,是让每一条 goroutine 都有清晰的起点、可控的终点和可追溯的资源归属。漏掉任意一环,都可能在线上放大成雪崩。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











