手把手寫一個線程池:基礎不好也能看懂(含詳細注釋)
很多人學 C++ 多線程,剛開始聽到“線程池”三個字,第一反應就是:太難了!線程+鎖+隊列,全是讓人頭大的東西,看得腦袋嗡嗡響。
但其實,如果我們一步步拆解,它比你想象的簡單得多。本篇文章會帶你從零開始,構建一個可以跑起來的最小線程池實現,所有代碼配有詳細解釋,哪怕你是初學者也能看懂。
一、線程池到底是什么?
我們先不講代碼,只講“本質”:
線程池的核心思想是:
“提前開好一批線程,任務來了就分配過去執行,用完不銷毀,反復利用。”
這樣可以:
- ? 避免頻繁創建/銷毀線程帶來的系統開銷
- ? 實現任務并發處理,提高 CPU 利用率
- ? 控制線程數量,防止線程過多造成資源搶占
二、線程池需要哪些組件?
為了實現一個最小可用的線程池,我們需要準備:
模塊 | 用途 |
| 存儲固定數量的線程 |
| 存儲待執行的任務 |
+ | 保證線程間安全、等待通知 |
標志 | 控制線程退出時機 |
接口 | 提供任務提交的方式 |
三、線程池的構造函數
我們從構造函數開始,重點:創建線程,并讓它們開始循環工作。
ThreadPool(size_t threadCount) : stop(false) {
for (size_t i = 0; i < threadCount; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queueMutex);
condition.wait(lock, [this] {
return stop || !tasks.empty();
});
if (stop && tasks.empty()) return; // 安全退出
task = std::move(tasks.front());
tasks.pop();
}
task(); // 執行任務
}
});
}
}
? 詳細解釋:
emplace_back([this] {...})
創建一個線程,執行一個死循環。condition.wait(...)
:如果沒有任務,就讓線程睡著,避免空轉。task = std::move(...)
:從任務隊列取一個任務出來。task()
:執行這個任務!
四、任務提交接口 submit
線程池是為任務服務的,我們需要一個函數來“扔任務進去”:
void submit(std::function<void()> f) {
{
std::unique_lock<std::mutex> lock(queueMutex);
if (stop) throw std::runtime_error("ThreadPool stopped");
tasks.emplace(std::move(f));
}
condition.notify_one(); // 喚醒一個線程執行任務
}
核心點:
- 支持任意無返回值任務(你可以傳入 lambda)
- 提交后立即喚醒一個“睡著”的線程去執行它
五、析構函數:資源回收
程序結束前,我們要優雅地關閉線程池:
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queueMutex);
stop = true;
}
condition.notify_all(); // 喚醒所有線程退出
for (std::thread& t : workers) {
if (t.joinable()) t.join();
}
}
做了什么?
- 設置
stop = true
,告訴線程池“別再等新任務了” - 喚醒所有線程,它們會自己判斷該退出
join()
等線程執行完再退出,避免野線程殘留
六、實際測試:線程池能不能用?
int main() {
ThreadPool pool(3); // 創建一個有3個線程的線程池
for (int i = 0; i < 6; ++i) {
pool.submit([i] {
std::cout << "任務 " << i << " 執行中\n";
std::this_thread::sleep_for(std::chrono::milliseconds(200));
});
}
std::this_thread::sleep_for(std::chrono::seconds(2));
}
你會看到控制臺輸出類似:
任務 0 執行中
任務 1 執行中
任務 2 執行中
任務 3 執行中
任務 4 執行中
任務 5 執行中
說明線程池真的“并發”跑了多個任務!
七、新手常見誤區提醒
- 忘記加鎖 → 多線程搶資源,數據錯亂
- 不喚醒線程 → 提交任務后卡死
- 主線程提前退出 → 子線程被強制終止
總結一句話
線程池的本質,就是用固定線程輪流干活,用隊列管理任務,用鎖和條件變量保證同步。
理解這些基本組件后,你就能輕松掌握多線程的核心套路。
附:完整類定義
class ThreadPool {
public:
ThreadPool(size_t);
void submit(std::function<void()>);
~ThreadPool();
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queueMutex;
std::condition_variable condition;
bool stop;
};