std::thread构造失败抛出std::system_error且code().value()为eagain(11),表明内核拒绝分配线程资源,主因是threads-max、rlimit_nproc或pid_max等系统级限制被触及,或mmap虚拟地址空间耗尽、栈过大导致内存碎片化。

std::thread 构造失败时抛出 system_error,错误码是 EAGAIN
Linux 对每个进程能创建的线程数有硬限制,通常由 /proc/sys/kernel/threads-max 和 RLIMIT_SIGPENDING 共同约束。当程序反复创建 std::thread 但未正确 join() 或 detach(),或线程栈过大导致内存碎片化,std::thread 构造函数会抛出 std::system_error,其 code().value() 为 EAGAIN(数值 11)。这不是业务异常,而是内核拒绝分配新线程资源的明确信号。
常见误判是把它当作逻辑错误忽略,或只捕获 std::exception 却没检查具体错误码。实际中,很多服务在高并发压测或长周期运行后突然无法新建线程,日志里只看到 “failed to create thread”,却查不到上下文——问题就卡在这一步没做错误码分支处理。
线程泄漏比线程爆炸更隐蔽,std::vector<:thread></:thread> 不清理就会累积
典型场景是把线程对象存进容器但忘记遍历 join():比如在循环中不断 threads.emplace_back(func, arg),却在函数退出前没调用 for (auto& t : threads) t.join();。此时每个未 join() 也未 detach() 的 std::thread 对象在析构时会调用 std::terminate(),但若该析构发生在子线程里(比如容器是局部变量、函数提前 return),主线程可能已退出,崩溃不落地,只留下孤儿线程和资源滞留。
-
std::thread移动后原对象变为不可 join 状态,但不会自动释放底层资源——必须显式处理 - 使用
std::jthread(C++20)可自动join(),但要注意它不解决栈空间耗尽问题 - 用
ps -T -p $(pidof your_app) | wc -l可实时查看当前线程数,对比cat /proc/sys/kernel/threads-max判断是否逼近上限
栈大小设置不当会加速线程耗尽,pthread_attr_setstacksize 需谨慎调大
Linux 默认线程栈大小约 8MB。如果代码里用 pthread_attr_setstacksize 把栈设成 64MB,单个进程最多只能创建约 128 个线程(假设系统允许 8192 线程)。更麻烦的是,这些大栈内存未必连续分配,mmap 失败时也会返回 EAGAIN,表现和线程数超限完全一致,但根源是虚拟内存碎片。
Linux 性能分析与调优专家,覆盖 CPU、内存、磁盘 I/O、网络、内核参数、编译优化、容器/K8s。适用场景:系统卡顿/高负载、内存不足/OOM/Swap 高、CPU 异常/iowait 高。
实操建议:
- 避免手动调大栈;如真需大栈,优先改用堆分配(
std::vector替代大数组) - 用
ulimit -s查看当前栈限制,单位 KB;调试时可临时设小(如ulimit -s 2048)快速暴露问题 - 线程池必须预设最大并发数,并在构造时传入合理栈属性,而不是无脑 new thread
崩溃无 core dump?先确认 ulimit -i 和 /proc/sys/kernel/pid_max 是否被触及
除了线程数本身,Linux 还限制每个进程的“任务数”(task count),包括线程和轻量级进程,由 RLIMIT_NPROC 控制,对应 ulimit -i。若该值设为 1024,而你的程序已创建 1023 个线程 + 1 个主线程,再建第 1025 个就会失败,报错仍是 EAGAIN,但原因不是 threads-max 而是用户级进程限额。
另一个容易被忽略的点是 /proc/sys/kernel/pid_max,它限制整个系统的 PID 总数。当系统长期运行且频繁 fork/thread,PID 耗尽后新线程也会创建失败。此时 cat /proc/sys/kernel/pid_max 和 ps -eL | wc -l 的差值接近 0 就是强信号。
真正棘手的地方在于:这三个限制(threads-max、RLIMIT_NPROC、pid_max)的错误表现完全相同,必须逐层排查,不能只盯一个配置项。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










