高併發伺服器中,頻繁建立/銷毀執行緒開銷巨大。線程池預先建立執行緒、複用處理任務,能大幅提升吞吐量。本文詳細闡述通用線程池的架構設計:任務物件抽象、執行緒動態伸縮策略、任務分配機制、異常安全,並附偽代碼流程與效能測試分析。
| 維度 | 核心問題 | 解決方案 |
|---|---|---|
| 任務物件通用性 | 業務邏輯千差萬別 | 純虛函數 run() 介面,衍生類別實作具體邏輯 |
| 執行緒創建/銷毀策略 | 何時增減執行緒 | 初始 N1、擴容 N3、閒置超時銷毀 N4,保留基線 N1 |
| 任務分配策略 | 輕量任務分配開銷大 | 批次派發:單執行緒連續處理 N5 個任務 |
class Task {
public:
Task();
virtual ~Task();
virtual bool run() = 0; // 返回 true=任務完成銷毀, false=繼續執行
};
new 動態建立,傳入線程池run() 返回值決定run() 實作邏輯N1 = 初始執行緒數 = N2 × n1 (N2 = 平均單次業務任務數)N3 = 擴容數量 = N2 × n2 (全忙時)N4 = 縮容數量 = N2 × n3 (閒置超時)T = 閒置超時閾值THREAD_MAX = 硬性上限輕量任務 (如 5ms) 下,單次派發開銷佔比高。解決:單執行緒連續處理 N5 個任務。
N5 = 1:逐個派發N5 > 1:批次派發,減少鎖競爭、上下文切換N5 = 5 時吞吐量近乎翻倍
for (;;) {
pThread = GetIdleThread();
if (pThread) {
if (CheckNewTask()) {
TaskList tl;
GetTask(tl, N5); // 取 N5 個任務
AddTaskToThread(pThread, tl);
continue;
}
}
if (!pThread && nThread < THREAD_MAX)
CreateNewThread();
// 無任務或達上限 → 等待
if (WaitForTaskOrThreadTimeout()) {
if (IncrIdleTime() > IDLE_MAX)
DecrIdleThread(); // 縮容
} else
return 0; // 關閉信號
}
for (;;) {
if (!CheckTaskQueue()) {
pPool->OnTaskIdle(this); // 通知池:我閒置了
if (WaitForTask()) continue;
else return 0; // 收到關閉信號
}
pTask = GetTask();
try {
while (!pTask->Run()) { /* 空迴圈,持續執行直到完成 */ }
} catch (...) {
WriteLog(...); // 異常隔離,不讓單一任務摧毀服務
}
delete pTask; // 任務完成,釋放
}
try-catch(...) 包裹 pTask->Run()delete pTask 在正常/異常路徑都執行
| 參數組 | N2 | N5 | 相對效能 |
|---|---|---|---|
| 參數 1 | 1 | 1 | 基準 |
| 參數 2 | 5 | 1 | 略優於參數 1 |
| 參數 3 | 5 | 5 | 約 2 倍於參數 1/2 |
結論:對輕量任務,N5 (批次大小) 影響遠大於 N2 (業務任務數)。批次派發是關鍵優化點。
N1, N2, N3, N4, N5, n1, n2, n3std::variant/std::any 替代虛函數實現無侵入任務thread_local 儲存、工作竊取 進階調度線程池非銀彈,需根據任務特性 (CPU/IO 密集、輕/重量級、優先級) 精細調參。本文架構提供可擴展骨架:任務介面解耦業務、動態伸縮適應負載、批次派發優化輕量任務、異常隔離保證服務可用性。