set scheduler-locking step最常用且安全,仅单步时冻结其他线程;on更激进,任何运行态都只允许当前线程执行,但可能致假死;二者需配合线程专属断点和显式thread n使用,否则无法精准隔离目标线程行为。

默认情况下,GDB 的 next、step、continue 等命令会让所有线程一起跑 —— 这在多线程调试中极易干扰观察,尤其当你只想盯住一个线程时。 关键不是“能不能用”,而是“用错 mode 会导致行为完全不符合预期”:比如你以为 set scheduler-locking on 能锁死所有其他线程,结果发现 finish 还是触发了线程切换;又或者设成 step 后单步很稳,但一按 c 就全活了,还撞上别的线程的断点,当前线程直接被切走。
什么时候该用 set scheduler-locking step
这是最常用也最安全的模式,专为单步调试设计。它只在你执行 next 或 step 时冻结其他线程,保证你看到的每一步都是当前线程的、干净的执行流。
- 适合场景:排查竞态条件、检查某线程内变量如何被修改、确认临界区是否被正确进入/退出
- 注意:
until、finish、return不受锁定保护 —— 它们会放行其他线程,只要其中任一线程命中断点,GDB 就会把那个线程设为当前线程 - 常见误操作:设了
step后用c继续,结果程序停在另一个线程的断点上,误以为“锁失效了”,其实是模式本就不覆盖continue
set scheduler-locking on 的真实行为
它比 step 更激进:只要 inferior(被调程序)处于运行状态(不管你是 next、continue 还是 finish),就只允许当前线程执行 —— 其他线程真的一行代码都不会跑。
- 适合场景:你想严格隔离一个线程的行为,比如验证某个线程是否会因等待锁而永久阻塞,且不希望其他线程推进造成干扰
- 风险点:如果当前线程卡在系统调用(如
read()、pthread_cond_wait())或无信号可唤醒的等待中,整个程序会“假死”,你无法靠 Ctrl+C 中断回来 —— 因为信号可能被发给了休眠线程,而它被锁住了 - 必须前置动作:执行前务必用
thread N显式切到目标线程,否则锁的是你上次停留的任意线程(常是主线程,而非你真正关心的那个)
为什么 break location thread id 和 set scheduler-locking 要配合用
断点本身是全局的:哪怕你只对线程 3 设了断点,线程 1/2/4 一旦执行到同一地址也会停。而 set scheduler-locking 控制的是“继续运行时谁可以动”,两者解决的是不同层面的问题。
- 典型组合:先
break main.cpp:42 thread 3,再thread 3→set scheduler-locking step,之后单步时线程 3 独占执行权,其他线程既不会意外停在 42 行,也不会在你单步时偷偷改共享变量 - 容易踩的坑:只设了线程专属断点,却没开 scheduler-locking —— 线程 3 停下后,你一按
n,线程 1/2 可能已经把共享缓冲区清空了,导致你看到的变量值完全失真 - 性能提示:开启 locking 不影响断点命中逻辑,但会显著降低多线程并发度 —— 别在线上环境用,仅限本地调试
真正难的不是记住三个 mode 的字面意思,而是理解 GDB 在每种 mode 下对“运行权”的实际裁决逻辑:它不控制线程创建/销毁,不干预调度器,只在每次从暂停态恢复执行前做一次“放行名单”判断。这个判断发生在命令执行瞬间,且对不同命令有不同策略 —— 忽略这点,on 和 step 在你手里就是两把钝刀。











