You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

资源充足时_thread.start_new_thread()抛出can't start new thread异常求助

解决_thread.start_new_thread()抛出"can't start new thread"异常的问题

首先,你遇到的这个问题虽然看起来矛盾(资源限制充足、只创建一个线程),但背后可能有几个容易忽略的系统级或代码层面的原因,我结合你提供的系统信息和代码来逐一分析:

可能的异常原因

1. 系统全局线程数或PID限制

虽然你的用户进程/线程数远未达到用户级限制,但Linux内核有全局线程总数限制(threads-max)和PID最大值限制(pid_max)——因为每个线程本质上是一个轻量级进程,会占用一个PID。你当前系统总线程数是452,这个数字看起来不大,但如果你的内核threads-max设置得比较保守(比如部分定制系统会调小默认值),就可能触发这个错误。

2. 过大的线程栈空间导致虚拟地址空间不足

你的用户栈大小限制是102400000字节(约100MB),这个数值远大于Linux默认的8MB栈大小。每个新线程都会从进程的虚拟地址空间中分配这么大的栈区域,即使物理内存足够,虚拟地址空间的碎片化或总量限制(尤其是32位系统)也可能导致分配失败。你当前进程已有9个线程,每个占100MB栈,再加上主线程,已经占用近1GB的虚拟地址空间,如果是32位系统,剩余的地址空间可能不足以分配新的100MB栈。

3. 线程资源未及时回收(隐性泄漏)

虽然你只显式创建一个线程,但如果之前的线程因为异常(比如reportDone()中ws.send()失败抛出未捕获的异常)没有正常退出,或者系统线程资源回收有延迟,也可能导致看似可用的资源实际上被占用。

4. 底层_thread模块的局限性

_thread是Python最底层的线程模块,缺少高层封装的错误处理和资源管理机制,相比threading模块更容易因为细微的系统资源问题抛出模糊的异常。

如何获取更多异常细节

1. 检查内核级限制

执行以下命令查看系统全局线程和PID限制:

# 查看系统最大线程数
cat /proc/sys/kernel/threads-max
# 查看系统最大PID数
cat /proc/sys/kernel/pid_max
# 查看当前系统已使用的PID数
ls /proc | grep -E '^[0-9]+$' | wc -l

如果threads-max接近当前总线程数(452),或者PID数接近pid_max,那就是全局限制的问题。

2. 捕获异常并打印详细上下文

在调用start_new_thread的地方添加异常捕获,结合traceback和系统进程信息工具(比如psutil)打印更多细节:

import traceback
import psutil
import os
import logging

def startJob(command, args):
    try:
        _thread.start_new_thread(doStartJob, (command, args))
    except Exception as e:
        logging.error(f"Failed to start thread: {str(e)}")
        # 打印完整的异常栈
        logging.error("Full traceback:")
        logging.error(traceback.format_exc())
        # 打印当前进程的线程数
        current_proc = psutil.Process(os.getpid())
        logging.error(f"Current threads in our process: {current_proc.num_threads()}")
        # 打印系统总线程数
        total_system_threads = sum(p.num_threads() for p in psutil.process_iter())
        logging.error(f"Total threads in system: {total_system_threads}")
        # 打印进程虚拟内存使用情况
        logging.error(f"Process virtual memory usage: {current_proc.memory_info().vms / 1024 / 1024:.2f} MB")

3. 查看系统内核日志

检查内核级的错误信息,比如内存分配失败、OOM Killer触发等:

dmesg | grep -i "out of memory\|thread\|pid"

解决建议

1. 调整线程栈大小

要么降低系统级的栈大小限制:

ulimit -s 8192  # 设置为8MB,接近默认值

要么在创建线程时显式指定更小的栈大小(_thread.start_new_thread的第四个参数):

# 指定1MB的栈大小
_thread.start_new_thread(doStartJob, (command, args), {}, 1024*1024)

2. 替换为threading模块

threading模块提供了更稳定的线程管理和错误处理,建议替换:

import threading

def startJob(command, args):
    thread = threading.Thread(target=doStartJob, args=(command, args))
    # 设置为守护线程,避免主线程退出时线程残留
    thread.daemon = True
    thread.start()

3. 修复线程内的异常处理

在doStartJob函数中添加异常捕获,避免线程因未处理异常而异常终止导致的资源泄漏:

def doStartJob(command, args):
    try:
        process = subprocess.Popen([command] + args, stdout=out, stderr=err, preexec_fn=os.setpgrp)
        process.wait()
        reportDone()
    except Exception as e:
        logging.error(f"Thread execution failed: {str(e)}")
        logging.error(traceback.format_exc())

4. 调整内核全局限制(如果需要)

如果确实是threads-max太小,可以临时调整:

# 临时设置为10000
echo 10000 > /proc/sys/kernel/threads-max

要永久生效,需要修改/etc/sysctl.conf添加kernel.threads-max = 10000,然后执行sysctl -p。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:55:02