死锁发生时程序已终止,无法访问pprof;唯一线索是panic日志末尾的goroutine调用栈。goland中需提前配置pprof和trace:导入_net/http/pprof_、启动http服务、设gotraceback=all,再用go tool trace定位chan recv/send阻塞或mutex重复加锁问题。

死锁发生时pprof根本用不上
看到 fatal error: all goroutines are asleep - deadlock! 就别翻 pprof 页面了——程序已终止,HTTP server 没机会响应请求,/debug/pprof/goroutine 接口压根访问不到。唯一可信线索是 panic 日志末尾列出的 goroutine 调用栈。GoLand 里跑起来直接崩溃,控制台输出就是全部证据。
GoLand 中必须提前加 pprof 启动项(仅用于非死锁场景)
如果想在 GoLand 里用 pprof 查“卡住但没 panic”的问题(比如 goroutine 长期阻塞在 channel receive、mutex.Lock),得手动配置启动参数和代码:
- 在
go.mod所在目录确保已导入:import _ "net/http/pprof" - main 函数开头加:
go http.ListenAndServe("127.0.0.1:6060", nil)(注意绑定127.0.0.1,别用localhost或:6060,GoLand 的调试环境有时 resolve 不了 localhost) - GoLand 的 Run Configuration → Program arguments 里**不要填任何东西**;但要勾选 “Allow running in parallel”,避免调试器卡住
- 运行后立刻在终端执行:
curl 'http://127.0.0.1:6060/debug/pprof/goroutine?debug=2',重点搜chan receive、chan send、sync.(*Mutex).Lock
GoLand 调试器配合 trace 工具更有效
pprof 只给快照,trace 能还原状态变迁。在 GoLand 里启用 trace 的实操要点:
GoLand 2026.1.1 是 2026.1 发布后的首个维护修正版本,适合已经开始体验 2026.1 新功能并希望同步补丁的开发者。它更适合用于入门项目、现有项目迁移测试和 IDE 行为验证。
- 确保代码里有
import _ "net/http/pprof"和go http.ListenAndServe("127.0.0.1:6060", nil) - Run Configuration → Environment variables 加上:
GOTRACEBACK=all(增强堆栈可读性) - 程序启动后,在终端执行:
go tool trace http://127.0.0.1:6060/debug/trace?seconds=5,会生成trace.out - 然后执行:
go tool trace trace.out,浏览器自动打开 Web UI → 切到 “Goroutine” 标签页 → 筛选状态为chan recv或chan send - 若某 goroutine 长时间处于
chan recv且无对应 sender 出现,基本就是死锁源头;无缓冲 channel 最容易暴露这点
GoLand 里最容易忽略的两个坑
一是 init 函数里起的 goroutine 容易静默卡死:如果 A 包的 init 启动 goroutine,又依赖 B 包的 init 完成,而 B 包 init 里又等 A 包某个变量初始化,就会在 panic 日志里看不到 channel 相关线索,只显示 runtime.gopark —— 这种情况必须人工扫所有包的 init 函数。
二是 mutex 卡死不触发 deadlock panic:goroutine 在持有锁时再次调用 mu.Lock(),会停在 runtime_SemacquireMutex,Go runtime 不认为这是“全部阻塞”,所以不会报 all goroutines are asleep。这种问题只能靠 /debug/pprof/goroutine?debug=2 里找重复出现的 sync.(*Mutex).Lock,或者用 go tool trace 看是否长期处于 sync.Mutex.Lock 状态。










