Python报错Can't start new thread但系统资源充足的问题咨询
问题背景:
尝试启动Python线程时,在threading.py文件的第745行触发错误:can't start new thread。但排查发现系统资源充足:
当前系统线程总数远低于系统上限:$ # Current Number of Threads: $ ps -fLA|wc -l 3996 $ # Max system threads $ cat /proc/sys/kernel/threads-max 261270磁盘空间也十分充裕:
$ df -h Filesystem Size Used Avail Use% Mounted on udev 16G 0 16G...
这种情况确实挺闹心的——明明全局线程上限还剩一大截,却卡在线程创建这一步,我给你梳理几个大概率的排查方向,按优先级来:
1. 先查进程级线程/进程数限制(ulimit)
系统级的threads-max是全局上限,但每个用户/进程能创建的线程数还受ulimit -u的限制(Linux里线程本质是轻量级进程,这个参数控制的是用户可打开的进程/线程总数)。
先查当前shell的限制:
ulimit -u
再精准查你的Python进程的限制(替换[PID]为你的Python进程ID):
cat /proc/[PID]/limits | grep "Max processes"
如果这个数值接近你当前的线程数(比如4000左右),那就是这个限制卡了你。解决办法是修改/etc/security/limits.conf调高限制:
* soft nproc 65535 * hard nproc 65535
修改后需要重新登录或者重启Python进程生效。
2. 检查线程栈内存是否耗尽
每个线程默认会分配8MB的栈内存(Linux默认值),4000个线程就是32GB——如果你的系统物理内存没这么多,或者可用内存被缓存/其他进程占满,哪怕free显示还有剩余,也会因为内存不足创建失败(因为线程栈需要连续的内存块)。
先查当前栈大小:
ulimit -s
如果是8192(单位KB,也就是8MB),可以尝试调小栈大小:
- 临时生效(当前shell启动的进程有效):
ulimit -s 1024 # 设置为1MB - 在Python代码里全局设置(需要在创建线程前调用):
import threading threading.stack_size(1024 * 1024) # 1MB栈大小
⚠️ 注意:栈太小可能导致递归较深的代码触发栈溢出,要根据你的业务逻辑调整合适的大小。
3. 排查代码中的线程资源泄漏
有没有可能你的代码没有正确回收线程?比如:
- 创建了线程但没调用
join(),导致线程资源一直占用? - 用了
daemon=True的后台线程,但主线程退出时没有正确清理? - 第三方库偷偷创建了线程但没释放?
你可以用下面的命令查看这个Python进程下的实际线程数,确认是不是真的接近4000:
ps -T -p [PID] | wc -l
如果线程数确实很高,就需要梳理代码里的线程管理逻辑,检查是否有未释放的线程资源。
4. 检查内核内存映射区限制
每个线程可能会占用几个内存映射区,当线程数较多时,可能会触发vm.max_map_count的限制(默认一般是65530)。
先查当前值:
cat /proc/sys/vm/max_map_count
如果数值偏小,可以临时调高:
echo 262144 > /proc/sys/vm/max_map_count
要永久生效的话,修改/etc/sysctl.conf:
vm.max_map_count=262144
然后执行sysctl -p使配置生效。
5. 定位底层系统错误码
如果上面的方法都没找到问题,可以直接看底层系统调用的错误码——Python的threading模块底层调用的是系统的clone()或pthread_create()函数,你可以用ctypes直接调用查看具体错误:
import ctypes from ctypes import get_errno libc = ctypes.CDLL('libc.so.6') # 尝试创建一个空线程,获取错误码 ret = libc.clone(ctypes.c_void_p(0), 0, 0, 0, 0, 0, 0) errno = get_errno() print(f"系统调用返回值: {ret}, 错误码: {errno}")
然后根据错误码查对应的问题(比如11是EAGAIN表示资源暂时不足,12是ENOMEM表示内存不足)。
内容的提问来源于stack exchange,提问作者speedplane




