dlv需在断点触发后执行goroutines命令才能列出全部协程;切换goroutine用goroutine ID,后续命令作用于该协程;闭包变量需通过frame和解引用查看;waiting状态需print (*runtime.g)(ID).waitreason查阻塞原因。

dlv怎么查看当前所有goroutine列表
dlv启动后默认停在入口点,此时goroutines命令才能列出全部协程。如果程序已运行、断点没打对,可能只看到主线程的runtime.main,看不到其他活跃goroutine。
- 必须先触发断点(比如
break main.main再continue),或用ctrl+c中断正在运行的程序,否则goroutines返回空或不全 -
goroutines输出里每行开头的数字是goroutine ID(如1、17),后面跟着状态(running、waiting、syscall等)和栈顶函数 - 加
-t参数可限制输出长度:goroutines -t 5只显示栈顶5层,避免刷屏
如何切换到指定goroutine并检查它的变量
用goroutine <id></id>切换上下文后,所有后续的print、locals、stack都作用于该goroutine——这点容易被忽略,切完不确认当前goroutine ID就查变量,结果查的是上一个的。
- 切换前先记下当前ID(
goroutine不带参数会显示当前ID),切换后立刻执行goroutine确认是否成功 - 局部变量可能被编译器优化掉(尤其未使用或逃逸分析后移入堆),
print v报could not find symbol时,试试print &v或加go build -gcflags="-N -l"重新编译 - 切换后
stack显示的是该goroutine的调用栈,但注意:它未必是“阻塞点”,只是当前PC位置;比如处于runtime.gopark说明在等channel、锁或timer
为什么dlv里看不到goroutine里的闭包变量
闭包变量实际存储在堆上,由编译器生成的隐藏结构体持有,dlv默认不展开这类匿名结构体字段,locals里直接看不到。
- 先用
stack定位到闭包调用帧(如main.main.func1),再用frame <n></n>跳转过去 - 然后
print *$frame(注意$frame是dlv内置变量,代表当前帧地址)可能看到闭包结构体指针,再print *$frame.0逐级解引用 - 更稳的方式是:在闭包内设断点,让变量“活”在栈帧里,此时
locals大概率能列出来;否则只能靠memory read硬扒,不推荐
goroutine状态为“waiting”但查不到阻塞点怎么办
常见于channel收发、mutex等待、time.Sleep等场景,dlv显示waiting但stack只到runtime.gopark,没法直接看出等谁——因为阻塞信息存在goroutine结构体的waitreason字段里,dlv不自动解析。
立即学习“go语言免费学习笔记(深入)”;
- 手动读取:
print (*runtime.g)(<id>).waitreason</id>(把<id></id>替换成goroutine ID),输出类似chan receive、semacquire、timer goroutine waiting - 注意:这个字段是Go 1.14+才稳定导出的字符串,旧版本得看
g.waiting布尔值+结合栈顶函数猜 - 若
waitreason为空,大概率是被runtime.Gosched主动让出,或刚创建还没调度,此时stack顶部通常是runtime.goexit
goroutine调试真正麻烦的不是命令记不住,而是状态和代码逻辑脱节——比如看到waiting却不知道等哪个channel,或者切换后print返回<optimized out></optimized>还以为是dlv bug。这些都得回退到编译选项和断点位置去调。










