fatal error 表明所有 goroutine 死锁,应立即查看 panic 输出的完整 goroutine 堆栈;勾选“Show command line afterwards”确保可见,重点检查 main goroutine 是否卡在 channel 操作。

看到 fatal error: all goroutines are asleep - deadlock! 就停手查堆栈,别点“Resume”或加 sleep
GoLand 里运行程序一旦打出这行 panic,说明 runtime 已判定所有 goroutine 进入不可唤醒阻塞态,进程即将退出——此时点击调试器的 Resume 或在代码里加 time.Sleep 不仅无效,还会掩盖真实堆栈。关键不是让程序“多跑一会儿”,而是立刻捕获 panic 输出里的 goroutine 列表。
- 确保 GoLand 的 Run Configuration 中勾选了 “Show command line afterwards”,这样终端输出不会被折叠,你能完整看到 panic 后的 goroutine 堆栈
- 重点关注
maingoroutine 停在哪:如果卡在ch 、<code>、<code>wg.Wait()或mu.Lock(),问题基本就在这行附近 - 扫其他 goroutine 是否全停在
runtime.chansend或runtime.gopark+chan receive—— 如果一堆都在 send,但没一个在 recv,就是发送方没人收 - 特别注意 init 函数里起的 goroutine:堆栈显示
init+go func,又依赖另一个包的 init 完成,会静默卡死,不报 channel 相关信息
用 GoLand 内置 Terminal 跑 GODEBUG=schedtrace=1000 快速确认是否真全员阻塞
不是所有“卡住”都触发死锁 panic;有些 goroutine 还在 running 状态(比如卡在 os.ReadFile),但调度器已失衡。这时靠 panic 日志不够,得看调度快照。
- 在 GoLand 底部 Terminal 执行:
GODEBUG=schedtrace=1000 go run main.go(注意不是GODEDEBUG) - 每秒输出一行
SCHED摘要,盯两个字段:g(goroutine 总数)和runnable(可运行数) - 如果
g长期为 1 且runnable为 0,而你代码本该启动多个 goroutine,说明其余全卡死或已退出 - 若
waiting: N长期不变,基本可断定是死锁;若runnable有值,说明还有活口,得查 channel 关闭、context cancel 或 WaitGroup 计数
在 GoLand 里启用 pprof 并直接跳转到阻塞 goroutine
pprof 在死锁发生后根本用不上——panic 一出,HTTP server 没机会响应 /debug/pprof/goroutine。但它对“卡住但没 panic”的场景极有用,而且 GoLand 支持一键跳转到源码行。
GoLand 2026.1.1 是 2026.1 发布后的首个维护修正版本,适合已经开始体验 2026.1 新功能并希望同步补丁的开发者。它更适合用于入门项目、现有项目迁移测试和 IDE 行为验证。
- 提前在
main函数开头加:go http.ListenAndServe("localhost:6060", nil),并导入import _ "net/http/pprof" - 运行程序后,在 GoLand Terminal 执行:
curl 'http://localhost:6060/debug/pprof/goroutine?debug=2' | grep -E "(chan send|chan receive|sync\.\*\(\*Mutex\)\.Lock)" - 复制匹配行里的文件路径和行号(如
main.go:42),GoLand 会自动高亮并允许 Ctrl+Click 跳转 - 别信
/debug/pprof/block:它统计的是“耗时阻塞”,而死锁可能一秒就 panic,这个 endpoint 对死锁帮助很小
导出 trace.out 并用 GoLand 自带的 Trace Viewer 分析 channel 阻塞链
pprof 只给快照,go tool trace 才能还原状态变迁——谁在等谁、等了多久、有没有被唤醒过。GoLand 2023.3+ 内置了 Trace Viewer,不用切终端。
- 确保已导入
import _ "net/http/pprof",并在main开头起服务:go http.ListenAndServe("localhost:6060", nil) - 运行程序后,访问
http://localhost:6060/debug/trace?seconds=5下载trace.out - 在 GoLand 中右键
trace.out→ “Open in Trace Viewer” → 切到 “Goroutines” 标签页 - 筛选状态为
chan recv或chan send的 goroutine:某个 goroutine 长时间处于chan recv,且无对应 sender 出现,基本就是死锁源头 - 注意:无缓冲 channel 最容易暴露这种问题;带缓冲的 channel 可能只是“暂时满/空”,需结合上下文判断
GoLand 的调试器本身不检测死锁,真正起作用的是 Go 运行时的 panic 输出、调度器快照和 trace 数据。最容易被忽略的是:panic 日志被截断时,你不该手动滚动终端找全堆栈,而该用 GODEBUG=schedtrace=1000 看调度器是否真卡死;还有就是误把 os.Open 卡住当成死锁——文件 I/O 是可唤醒等待,不会触发 all goroutines are asleep。

















