| 因素 | 影響 |
| 堆疊實際提交 | 保留 ≠ 提交,但大量執行緒會耗盡提交額度 |
| 非分頁集區 | 每個執行緒核心物件 (KTHREAD) 消耗非分頁記憶體,系統上限受限 |
| 系統 PTE (Page Table Entries) | 大量執行緒消耗系統 PTE,導致配置失敗 |
| 物件管理器 | 執行緒物件在 \ObjectTypes\Thread,名稱空間有限 |
| 記憶體碎片 | 2GB 虛擬位址空間碎片化,無法配置連續 1MB 區塊 |
調整堆疊大小提升上限
- 連結器選項:
/STACK:524288 (512 KB) → 理論上限 ~4,096
- 建立執行緒時指定:
CreateThread(NULL, 256*1024, ...) (256 KB) → 理論 ~8,192
- 風險:堆疊過小易導致堆疊溢位 (遞迴、大區域變數、結構化例外處理)
64-bit 進程優勢
- 使用者模式位址空間:8 TB (Windows 8.1+) / 128 TB (Windows 10 1709+)
- 預設堆疊 1 MB 理論支援百萬級執行緒
- 實際仍受非分頁集區、PTE、排程器限制,但上限大幅提升
實測數據參考 (32-bit Windows)
預設 1MB 堆疊: ~1,800 - 2,000 執行緒即異常退出
調整 512KB: ~3,500 - 3,800
調整 256KB: ~6,000 - 7,000 (極易堆疊溢位)
物理記憶體 64GB:單進程仍受 2GB/3GB 虛擬空間限制,無直接幫助
伺服器端設計啟示
- 一連線一執行緒模型:硬體固定時,硬性上限 ~2,000 連線 (32-bit)
- 解決方案:
- 遷移 64-bit 進程
- 執行緒池 + 非同步 I/O (IOCP / Overlapped I/O)
- 多進程架構 (如上文 DataSnap 方案)
- 事件驅動 / 協程模型 (libuv、Boost.Asio、mORMot)
程式碼驗證:測試最大執行緒數
program ThreadLimitTest;
{$APPTYPE CONSOLE}
uses Windows, SysUtils, Classes;
var
Count: Integer = 0;
Handles: array of THandle;
function ThreadProc(Param: Pointer): Integer; stdcall;
begin
Sleep(INFINITE); // 保持執行緒存活
Result := 0;
end;
var
Id: DWORD;
h: THandle;
begin
SetLength(Handles, 10000);
try
while True do begin
h := CreateThread(nil, 0, @ThreadProc, nil, 0, Id);
if h = 0 then begin
WriteLn('CreateThread 失敗, 錯誤碼: ', GetLastError);
WriteLn('成功建立執行緒數: ', Count);
Break;
end;
Handles[Count] := h;
Inc(Count);
if Count mod 100 = 0 then Write('.');
end;
finally
// 清理
for var i := 0 to Count - 1 do CloseHandle(Handles[i]);
end;
ReadLn;
end.
關鍵結論
- 32-bit 進程受 2GB 虛擬位址空間硬性限制,預設堆疊下 ~2,000 執行緒為極限
- 物理記憶體大小不直接決定單進程執行緒上限
- 非分頁集區耗盡同樣導致建立失敗
- 高併發伺服器必須採用非同步 I/O、執行緒池或多進程架構
- 現代開發首選 64-bit + 非同步模型,徹底解決規模瓶頸
- 上一篇文章: Delphi 多進程架構解決 DataSnap TCP 長連線數量瓶頸
- 下一篇文章: 將存儲過程數據輸出至 HTML 報表模板:ASP/ADO 經典實現方案
Copyright ©
程式碼之家 All Rights Reserved