You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python报错Can't start new thread但系统资源充足的问题咨询

解决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}")

然后根据错误码查对应的问题(比如11EAGAIN表示资源暂时不足,12ENOMEM表示内存不足)。


内容的提问来源于stack exchange,提问作者speedplane

火山引擎 最新活动