go程序死锁会立即panic并打印“fatal error: all goroutines are asleep - deadlock!”;无缓冲channel发送前必须有接收方就位,for range需close否则无限等待,单goroutine先发后收必死锁。

Go 程序一旦死锁,运行时会立即 panic 并打印 fatal error: all goroutines are asleep - deadlock!,这不是卡顿,是程序彻底停摆——必须从 panic 日志和 goroutine 状态入手,而不是猜逻辑。
channel 无配对收发导致的死锁
无缓冲 channel 是死锁高发区:发送前必须有接收方就位,否则发送方永久阻塞;for v := range ch 前必须有人 close(ch),否则无限等待。
- 单个 goroutine 对无缓冲 channel 先发后收(或先收后发)必然死锁,例如:
ch := make(chan int); ch - 多个值发送但只启动一个接收 goroutine,且未用
range或ok判断关闭状态,第二次发送就会阻塞 - 用
select { case v := 可防阻塞,但 <code>default空转会高频轮询,不是真正解法 - 缓冲 channel 能缓解,但
len(ch) == cap(ch)才表示满、len(ch) == 0才表示空;无缓冲 channel 的长度永远是 0,不能靠len判断可写性
sync.RWMutex 读锁重入引发的隐式死锁
Go 标准库明确禁止在同一个 goroutine 中对 sync.RWMutex 多次调用 RLock(),也不允许在已持有读锁时再调用 Lock()。这种“看似安全”的重入会破坏锁的内部计数逻辑,最终让其他 goroutine 永久等待。
- 典型表现:goroutine 卡在
(*RWMutex).RLock或(*RWMutex).Lock,pprof 显示大量gopark等待 - 常见于缓存模块中,一个方法用
RLock读数据并启动 goroutine 发送 channel,而该 goroutine 内部又调用了同一把锁的RLock - 修复方式不是加锁粒度拆分,而是确保每个 goroutine 对同一把
RWMutex最多一次RLock+ 一次RUnlock,且不混用读写锁调用 -
go-deadlock这类第三方包可替换原生sync,在测试期提前暴露此类问题
panic 日志里看 goroutine 堆栈定位源头
死锁 panic 输出的 goroutine 列表是第一手线索,不是辅助信息——它直接告诉你谁在等什么。
- 重点关注
maingoroutine 停在哪一行:比如卡在mu.Lock()、wg.Wait()或 - 检查其他 goroutine 是否全卡在同一类操作上:如全部停在
chan send,说明 sender 没人接收;全部停在chan recv,说明 receiver 没人发 - 有没有 goroutine 卡在
select {}或空for {}?那是典型的“忘了退出条件”,不是资源争用,但也会触发死锁 panic - 日志被截断时,加环境变量运行:
GODEBUG=schedtrace=1000 go run main.go,每秒输出调度快照,观察G数量是否长期为 0
用 go tool trace 还原阻塞关系
pprof 只能看到栈帧,而 go tool trace 能还原 goroutine 状态变迁,一眼看出谁在等谁、等了多久、是否永久阻塞。
- 启动时导入
_ "net/http/pprof",并在main开头起服务:go http.ListenAndServe("localhost:6060", nil) - 运行后访问
http://localhost:6060/debug/trace?seconds=5下载trace.out - 执行
go tool trace trace.out,打开 Web UI,在 “Goroutine” 标签页筛选状态为chan receive或chan send的 goroutine - 关键线索:某 goroutine 长时间处于
chan recv状态,且无对应 sender 唤醒,基本就是死锁源头;注意无缓冲 channel 比带缓冲的更容易暴露问题
最容易被忽略的是:死锁 panic 只捕获“所有 goroutine 都睡着”的情况;如果一部分 goroutine 死锁、另一部分还在跑,运行时根本不会报错——这时它已退化为 goroutine 泄露,表现为协程数持续上涨、内存缓慢增长、CPU 却不高。这类问题得靠 pprof 的 /debug/pprof/goroutine?debug=2 手动抓堆栈,逐个排查挂起点。











