Go中常见死锁是向无缓冲channel发送或接收时无人配对,导致所有goroutine阻塞并panic;无缓冲channel要求发送与接收严格并发配对,同线程顺序执行必死锁。

channel 操作不匹配导致死锁
Go 中最常见死锁场景是 goroutine 向无缓冲 channel 发送数据,但没有其他 goroutine 在同一时刻接收;或从空 channel 接收,却无人发送。运行时检测到所有 goroutine 都阻塞且无法唤醒,就会 panic:fatal error: all goroutines are asleep - deadlock!
- 无缓冲 channel 必须「一发一收」严格配对,发送和接收必须并发发生
- 使用
select+default可避免永久阻塞,但不解决逻辑错配问题 - 若 sender 和 receiver 在同一线程(如 main)顺序执行,必然死锁:例如先
c 再,无 goroutine 并发参与
range
- 仅当所有 sender 都完成且明确不再发送时,才由最后一个 sender 或协调者调用
close() - receiver 不应 close channel —— Go 约定:sender 负责关闭
- 多个 sender 时,用
sync.WaitGroup或context协调关闭时机
sync.Mutex
- 不要在持有锁期间调用可能再次获取同一锁的函数(包括方法内嵌调用)
- 多锁场景下,始终按固定全局顺序加锁(如按 struct 字段地址排序,或约定
m1总在m2前) - 考虑用
sync.RWMutex替代,读多写少时可降低争用
WaitGroup 使用不当阻塞主 goroutine
sync.WaitGroup 的 Wait() 会阻塞直到计数归零。若 Add() 调用晚于 Go 启动,或漏掉 Done(),main 就永远卡住 —— 表现类似死锁,但实际是逻辑遗漏。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
Add()必须在go语句前调用(或至少在 goroutine 启动前完成),否则计数可能为 0 就进入Wait() - 每个 goroutine 必须确保执行一次
Done(),哪怕提前 return 或 panic,推荐用defer wg.Done() - 不要在循环中反复
Wait(),除非每次Add()/Done()都成对出现
func main() {
var wg sync.WaitGroup
for i := 0; i <p>死锁往往不是单点错误,而是多个同步原语在时序和职责上的隐含耦合。调试时别只盯 panic 位置,重点检查 channel 生命周期、锁作用域边界、以及 WaitGroup 计数是否真正反映并发意图。</p>
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










