<p>死锁发生时错误“fatal error: all goroutines are asleep - deadlock”表明所有goroutine因等待锁、channel或WaitGroup而阻塞;应通过SIGQUIT获取stack dump,重点排查semacquire、chan receive等阻塞状态,并严格遵循固定加锁顺序避免交叉等待。</p>

死锁发生时 fatal error: all goroutines are asleep - deadlock 怎么快速定位
Go 运行时抛出这句错误,说明所有 goroutine 都在等锁、channel 或 sync.WaitGroup,没人能推进。不是代码没跑,是卡死了。
最直接的排查方式是加 runtime.SetBlockProfileRate(1) + pprof.Lookup("block").WriteTo(os.Stdout, 1),但日常开发更推荐:启动时加 -gcflags="-l" 关闭内联,再用 go run -gcflags="-l" main.go 配合 Ctrl+\ 发送 SIGQUIT,看 goroutine stack dump —— 所有阻塞点会清清楚楚列出来,重点关注状态为 semacquire(锁)、chan receive(channel)、sync.runtime_SemacquireMutex 的 goroutine。
- 别只看报错位置,要顺藤摸瓜找谁在等谁
- 如果用了
sync.Mutex,检查是否在 defer 前就 panic 导致 unlock 没执行 - channel 操作前先确认容量和关闭状态,
select中带default可避免无限等待
多个 sync.Mutex 一起用时,为什么必须固定加锁顺序
资源排序锁定(lock ordering)不是“建议”,是避免死锁的硬约束。两个 goroutine 分别按不同顺序获取 A、B 两把锁,就可能 A 等 B、B 等 A。
比如一个 goroutine 执行 muA.Lock(); muB.Lock(),另一个执行 muB.Lock(); muA.Lock(),只要时机凑巧,立刻死锁。Go 不像 Java 有 tryLock 或超时机制,Lock() 是阻塞到底的。
立即学习“go语言免费学习笔记(深入)”;
- 约定优先级:按 struct 字段声明顺序、变量名字母序、或业务层级(如
userMuorderMu paymentMu) - 把多锁逻辑封装成函数,内部统一顺序,外部只调用一次,不暴露单个锁
- 用
go vet无法检查顺序,但可以用注释强制约定,例如// lock order: dbMu → cacheMu → logMu
sync.RWMutex 和 sync.Mutex 混用时的隐性死锁风险
sync.RWMutex 的 RLock() 不会阻塞其他 RLock(),但会阻塞 Lock();反过来,Lock() 会阻塞所有读写。问题在于:你可能在读路径里嵌套写了写操作,而没意识到它正被读锁拦着。
典型场景:一个函数用 rwMu.RLock() 读配置,中间调用另一个函数尝试 rwMu.Lock() 更新缓存 —— 死锁当场发生,因为当前 goroutine 已持读锁,又想升级为写锁,但 Go 不支持锁升级。
- 绝不在持有
RLock()期间调用任何可能触发Lock()的函数 - 读写分离要彻底:读路径只读,写路径先
Unlock()再Lock()(如果之前有 RLock),或干脆拆成两个独立临界区 - 注意第三方库是否内部用了同一把
RWMutex,比如某些 cache 包的Get和Set方法
channel 关闭后继续接收或发送引发的伪死锁
channel 关闭后,send 会 panic,receive 会立即返回零值 + false。但如果你在 for range 中接收,而 sender 没按预期关闭 channel,receiver 就永远卡在 range 上 —— 表现和死锁一模一样,但实际是逻辑遗漏。
常见于 worker pool:主 goroutine 启动 N 个 worker 从 jobs channel 读任务,自己负责关 channel,但如果 job 数量动态变化或提前退出,close(jobs) 没执行,所有 worker 就挂住。
- 用
select+donechannel 控制生命周期,而不是依赖range自动退出 - sender 关闭 channel 前确保所有 sender 都已退出,可用
sync.WaitGroup协调 - 不要在多个 goroutine 中重复
close(ch),会 panic;可封装safeClose(ch)函数做原子判断
真正麻烦的不是锁本身,是人脑对并发路径的想象总比实际少走一两步。每次加锁前,默念一遍“谁可能同时在等我手里的这个资源”,比背一百条规则管用。










