为什么操作系统层面的线程数远多于Python代码实际创建的线程数?
问题原因与排查方案
线程数不一致的核心原因
你看到的差异主要来自两类线程,Python官方的threading模块只会统计通过threading.Thread接口创建、且尚未退出的活跃线程,剩下的2类线程不会被统计到:
- 未被回收的僵尸线程:如果你创建的非daemon线程运行结束后,主线程没有主动调用
join()方法回收资源,Python层会将该线程标记为非活跃,从active_count中移除,但操作系统层面的线程描述符资源不会被释放,会以僵尸/退出状态保留,ps命令仍然能统计到这些已经停止运行的非活跃线程。 - 底层依赖创建的非Python托管线程:你用到的C扩展、第三方库(比如numpy、PyTorch、数据库驱动、消息队列客户端)会直接调用系统接口
pthread_create创建线程,这些线程没有在Python的threading模块注册,所以完全不会被active_count、enumerate统计,这类线程可能是闲置的线程池 worker,也可能是正在运行的活跃线程。
剩余线程的状态判断
绝大多数未回收的僵尸线程都处于非活跃状态,底层依赖创建的线程大部分是闲置等待任务的非活跃线程,只有少部分是正在处理任务的活跃线程。
你可以执行以下命令直接查看所有线程的状态:
ps -T -p <替换为你的进程PID> -o tid,state,comm
输出中state字段对应状态:
Z:僵尸状态,完全非活跃S:休眠闲置状态,非活跃R:正在运行状态,活跃
查询线程对应函数名的方法
针对僵尸线程
这类线程已经退出运行,无法直接查询到原始执行的函数名,你可以通过以下验证方法确认是否是未回收导致的:
创建线程时设置daemon=True,线程结束后会自动释放所有资源:
thread1 = threading.Thread(target=foo, args=(arg1,), daemon=True) thread1.start()
或者在线程任务处理完成后主动调用join()回收,再观察操作系统统计的线程数是否回归正常,即可定位是不是僵尸线程导致的数量暴涨。
针对非僵尸的非托管线程
你可以通过调试工具直接抓取所有线程的调用栈:
- 用gdb附着进程查询全量调用栈
安装对应依赖后执行:
进入gdb交互界面后依次执行:gdb -p <替换为你的进程PID>info threads # 列出所有线程ID和基础状态 thread apply all bt # 打印所有线程的完整调用栈,包含C函数和Python函数 - 用py-spy采样调用栈
安装py-spy后执行以下命令,采样10秒内所有线程的运行栈:
不管是Python托管还是底层创建的线程,都能抓到对应的执行函数。py-spy record -p <替换为你的进程PID> --duration 10
内容的提问来源于stack exchange,提问作者texasraj
相关产品推荐
相关产品推荐

