必须同时设置"subProcess": true和"justMyCode": false,否则子线程断点不触发;前者启用对threading.Thread等派生线程的监听,后者防止调试器将线程函数误判为第三方代码跳过。

subProcess 必须设为 true,否则子线程断点永远不触发——这不是 bug,是 debugpy 的默认行为。
必须开启 subProcess 和 justMyCode
VSCode Python 扩展默认用 debugpy(不是已弃用的 ptvsd),但它只监控主线程,除非你显式告诉它“也管管子线程”。
-
"subProcess": true是硬性开关:让debugpy监听所有由当前进程派生的线程(包括threading.Thread启动的) -
"justMyCode": false是配套项:避免调试器把线程函数当成“第三方代码”跳过——尤其当目标函数是lambda或嵌套在标准库调用链里时 - 两者缺一不可;只开
subProcess会导致断点变成空心圆,只开justMyCode则子线程压根不进调试上下文 - 修改后必须重启整个调试会话,热重载不生效
断点位置和线程可见性
即使配置正确,你也看不到“所有线程变量堆在一起”的视图——VSCode 只显示当前选中的线程上下文。
- 断点必须打在子线程函数体第一行可执行语句上,比如
print("in thread"),而不是threading.Thread(target=...).start()那一行 - 调试暂停后,左侧 CALL STACK 面板会列出
MainThread、Thread-1、Thread-2等——点击任一名称,Variables 面板立即切换为其局部作用域 - 全局变量(模块级或
global声明的)在任意线程下都可见,但值是最新写入状态,不是快照,注意竞态 - 没暂停的线程(如卡在
time.sleep(5)中)不会出现在 CALL STACK 里,这是正常行为
多进程调试要额外满足三个条件
subProcess: true 对 multiprocessing 也生效,但仅靠它远远不够。
- 启动方式必须是
spawn(Windows/macOS 强制要求):import multiprocessing as mp; mp.set_start_method('spawn') - 子进程入口函数必须定义在模块顶层,不能嵌套在
if __name__ == '__main__':或其他函数内——否则debugpy找不到可导入路径,断点变空心圆 - 推荐加
"console": "integratedTerminal":避免子进程因缺少终端上下文静默退出,尤其在 spawn 模式下 - 如果子进程仍不触发断点,检查调试控制台顶部日志是否含
debugpy version 1.6.0+;若出现ptvsd或初始化失败,先运行pip uninstall ptvsd和pip install --upgrade debugpy
容易被忽略的细节
最常被跳过的不是配置项,而是线程生命周期本身。
立即学习“Python免费学习笔记(深入)”;
- 子线程运行太快(比如只做一次计算就退出),
debugpy来不及注入调试钩子——加time.sleep(0.1)延长存活窗口 - 不要在
threading.Thread启动后立刻调用.join(),这会让线程生命周期压缩到毫秒级,调试器几乎无法捕获 - 使用
concurrent.futures.ThreadPoolExecutor比裸threading.Thread更稳定,debugpy对其有显式支持 - macOS/Linux 用户若用虚拟环境,确保
launch.json中的python解释器路径指向该环境,否则subProcess可能加载错版本的debugpy


















