Does Windows have a limit of 2000 threads per process? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20050729-14/?p=34773
Raymond Chen 2005年07月29日
Windows 是否有一个每个进程2000线程的限制?
简要
文章解释了在 Windows 程序中创建超过 2000 个线程的限制并非由操作系统本身设定,而是由于每个线程需要分配栈空间,这会消耗大量的地址空间。通过减少每个线程的栈大小,可以增加线程的数量,但这种做法并不推荐,因为它会导致性能下降和潜在的稳定性问题。作者建议使用 I/O 完成端口和线程池等机制来更有效地处理大量并发任务。
正文
经常有人问为什么他们不能在一个进程中创建超过大约2000个线程。原因并不是 Windows 本身有任何特定的限制。相反,是程序员没有考虑到每个线程使用的地址空间量。
一个线程由一些在内核模式下的内存(内核栈和对象管理)、一些在用户模式下的内存(线程环境块、线程局部存储等)以及它的栈组成。(或者如果你在 Itanium 系统上,是栈们。)
通常,限制因素是栈的大小。
#include <cstdio>
#include <windows.h>
DWORD CALLBACK ThreadProc(void*) {
Sleep(INFINITE);
return 0;
}
int __cdecl main(int argc, const char* argv[]) {
int i;
for (i = 0; i < 100000; i++) {
DWORD id;
HANDLE h = CreateThread(NULL, 0, ThreadProc, NULL, 0, &id);
if (!h) break;
CloseHandle(h);
}
printf("Created %d threads\n", i);
return 0;
}
这个程序通常会打印出大约2000个线程的数量。
为什么在大约2000个线程时失败?
因为链接器分配的默认栈大小是 1MB,2000 个栈乘以每个栈 1MB 等于大约 2GB,这是用户模式程序可用的地址空间总量。
你可以通过减少栈大小来尝试将更多的线程压缩到你的进程中,这可以通过调整链接器选项或手动覆盖传递给 CreateThread
函数的栈大小来实现,如 MSDN 所述。
HANDLE h = CreateThread(NULL, 4096, ThreadProc, NULL,
STACK_SIZE_PARAM_IS_A_RESERVATION, &id);
通过这个改变,我能够勉强创建大约 13000 个线程。虽然这当然比 2000 个好,但离天真的期望 500000 个线程还有差距。(在 2GB 的地址空间中,一个线程使用 4KB 的栈。)但你忘记了其他的开销。地址空间分配粒度是 64KB,因此即使只使用了 4KB,每个线程的栈也占用 64KB 的地址空间。当然,你并不是可以自由地使用全部 2GB 的地址空间;还有系统 DLL 和其他东西占用了它。
但每当有人问,“一个进程可以创建的最大线程数是多少?”时,真正提出的问题其实是,“你为什么创建这么多线程,甚至于成了一个问题?”
“每个客户端一个线程”的模型众所周知,无法扩展到十几个客户端左右。如果你要同时处理这么多客户端,你应该转移到一个模型,不是为每个客户端分配一个线程,而是分配一个对象。(总有一天我会思考线程和对象之间的对偶性。)Windows 提供了 I/O 完成端口和线程池,帮助你从基于线程的模型转换为基于工作项的模型。
请注意,纤程在这里并没有太大帮助,因为纤程有一个栈,而且几乎总是栈所需的地址空间是限制因素。