注:该文用于个人学习记录和知识交流,如有不足,欢迎指点。
一、线程池是什么?
- 进程是操作系统进行资源分配的基本单位,线程是操作系统进行CPU调度的基本单位。
- 线程池是维持管理一定数量线程的池式结构:
- 维持就是复用、
- 管理就是控制线程工作和休眠,
- 一定数量是因为CPU核心数有限(线程池过多会增加CPU切换)
总的来说:线程池(Thread Pool)是一种预先创建一组可复用线程的资源管理机制,核心是维护一个线程集合,将任务提交到线程池后,由池中的空闲线程执行,避免频繁创建 / 销毁线程的开销,同时统一管理线程的生命周期
二、线程池解决的问题
异步执行耗时任务(不过度占用核心线程),充分利用多核。
耗时:
- 耗时等待(io):如落盘操作、向数据库请求数据
- 耗时计算:如加解密运算等
三、线程池是如何实现的呢?
在 C++ 中
可基于以下同步原语实现
std::thread:线程std::mutex:互斥锁std::condition_variable:条件变量
也可使用第三方库(如 Boost.ThreadPool)。
实现模板
- 生产消费模型:任务队列
- 生产者push任务到队列,唤醒一个休眠线程
- 消费者从任务队列里面取任务去执行
- 任务队列里面没有任务的话消费者会陷入休眠(线程阻塞)
问1:为什么采用队列?
- 职责:生产者对应着一个口、消费者对应着另一个口
- 队列的插入和读取O(1) 、 锁占用的时间短 、 锁的使用变得灵活
问2:线程池中的线程数量如何确定
- cpu密集型:
经验值:cpu核心数个线程数量。
最优值:经验值+1、+2比对性能得到- io密集型:
(线程等待时间+cpu运算时间)*cpu核心数/cpu运算时间
经验值:2*cpu核心数个线程数量(一半等待、一半执行)。
最优值:经验值+1、+2 比对性能得到- 性能测试(吞吐量):每秒执行的任务数量
问3:线程池的设计关键?
关键在于队列的设计和线程数量的管理。
1. 队列的设计
| 队列设计维度 | 对应的线程池类型 | 核心影响 |
|---|---|---|
| 队列边界(有界 / 无界) | 有界任务队列线程池 / 无界队列池 | 决定任务积压时的行为(阻塞 / 拒绝 / 溢出) |
| 队列排序规则 | 普通 FIFO 池 / 优先级线程池 | 决定任务执行的优先级(如 VIP 订单优先) |
| 队列时间属性 | 普通池 / 定时 / 延迟线程池 | 决定任务是否支持延迟 / 周期性执行 |
| 队列数据结构 | 普通队列池 / 批量任务池 | 决定是否支持任务批量提取 / 执行 |
| 队列的数量 | 生产者一个队列、消费者一个队列 | 通过一次队列的交换减少了锁的竞争 |
tips:FIFO (First In, First Out ) : 先进先出
举个典型例子:
- 「优先级线程池」和「普通固定线程池」的核心差异,就是队列从
std::queue(FIFO)换成了std::priority_queue(按优先级排序),线程数管理(固定)完全相同; - 「定时线程池」的核心改造也是队列 ------ 将普通队列替换为「按执行时间排序的优先级队列」,线程会定时检查队列头部任务是否到执行时间;
- 「有界任务队列线程池」和「无界固定线程池」的唯一差异,就是队列设置了最大容量,队列满时提交任务的线程会阻塞 / 拒绝任务。
2. 线程数量的管理
| 基础线程池类型 | 核心区分依据(线程数量管理) |
|---|---|
| 固定线程池 | 线程数初始化后固定,永不增删 |
| 缓存线程池 | 线程数动态增减(无核心线程,空闲超时销毁) |
| 单线程池 | 线程数固定为 1,任务串行执行 |
四、代码实现:
关键知识点
- lock_guard与unique_lock
- lock_guard 无法手动释放锁
- unique_lock 可以手动释放锁
- std::condition_variable not_empty_;
- not_empty_.wait(lock, condi_func):释放锁,然后休眠,等待唤醒判断条件是否满足 (满足则获取锁,否则继续休眠)
- not_empty_.notify_one(): 唤醒wait中的一个线程
- not_empty_.notify_all() :唤醒wait中的所有线程,常用于销毁线程池
核心代码:
1. 固定线程池
threadpool.h
cpp
#pragma once
#include <thread>
#include <functional>
#include <vector>
// #include "blockingqueue.h" //避免循环依赖(头文件扩散)!!!!!,只在源文件中引用
// 前置声明
// blockingqueue 仅仅只能用作指针或引用
template <typename T>
class BlockingQueue;
class ThreadPool {
public:
// 初始化线程池
explicit ThreadPool(int threads_num);
// 停止线程池
~ThreadPool();
// 发布任务到线程池
void Post(std::function<void()> task);
private:
void Worker();
std::unique_ptr<BlockingQueue<std::function<void()>>> task_queue_; // 拷贝构造会报错 --> 防止外部调用拷贝构造
std::vector<std::thread> workers_;
};
threadpool.cc
cpp
#include "blockingqueue.h"
#include <memory>
#include "threadpool.h"
ThreadPool::ThreadPool(int threads_num) {
task_queue_ = std::make_unique<BlockingQueue<std::function<void()>>>();
for (size_t i = 0; i < threads_num; ++i) {
workers_.emplace_back([this] {Worker();});
}
}
// 停止线程池
ThreadPool::~ThreadPool() {
task_queue_->Cancel();
for(auto &worker : workers_) {
if (worker.joinable())
worker.join();
}
}
void ThreadPool::Post(std::function<void()> task) {
task_queue_->Push(task);
}
void ThreadPool::Worker() {
while (true) {
std::function<void()> task;
if (!task_queue_->Pop(task)) {
break;
}
task();
}
}
2. 队列的设计
blockingqueue.h
2.1. 单队列
生产者和消费者共用一个队列,意味着锁的竞争频繁

cpp
#pragma once
#include <condition_variable>
#include <functional>
#include <queue>
#include <mutex>
#include <thread>
template <typename T>
class BlockingQueue {
public:
BlockingQueue(bool nonblock = false) : nonblock_(nonblock) { }
// 入队操作
void Push(const T &value) {
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(value);
not_empty_.notify_one(); // 唤醒 not_empty_.wait(lock, [this]{ return !queue_.empty() || nonblock_; });处的worker
}
// 正常 pop 弹出元素
// 异常 pop 没有弹出元素
bool Pop(T &value) {
std::unique_lock<std::mutex> lock(mutex_);
// 1. mutex_.unlock()
// 2. queue_empty && !nonblock 线程在 wait 中阻塞
// notify_one notify_all 唤醒线程
// 3. 假设满足条件 mutex_.lock()
// 4. 不满足条件 回到 2
not_empty_.wait(lock, [this]{ return !queue_.empty() || nonblock_; }); // 线程休眠
if (queue_.empty()) return false;
value = queue_.front();
queue_.pop();
return true;
}
// 解除阻塞在当前队列的线程
void Cancel() {
std::lock_guard<std::mutex> lock(mutex_);
nonblock_ = true;
not_empty_.notify_all();
}
private:
bool nonblock_;
std::queue<T> queue_;
std::mutex mutex_;
std::condition_variable not_empty_;
};
2.2 双队列
生产者一个队列、消费者一个队列

cpp
#pragma once
#include <condition_variable>
#include <functional>
#include <queue>
#include <mutex>
#include <thread>
template <typename T>
class BlockingQueuePro {
public:
BlockingQueuePro(bool nonblock = false) : nonblock_(nonblock) {}
void Push(const T &value) {
std::lock_guard<std::mutex> lock(prod_mutex_);
prod_queue_.push(value);
not_empty_.notify_one();
}
bool Pop(T &value) {
std::unique_lock<std::mutex> lock(cons_mutex_);
if (cons_queue_.empty() && SwapQueue_() == 0) {
return false;
}
value = cons_queue_.front();
cons_queue_.pop();
return true;
}
void Cancel() {
std::lock_guard<std::mutex> lock(prod_mutex_);
nonblock_ = true;
not_empty_.notify_all();
}
private:
int SwapQueue_() {
std::unique_lock<std::mutex> lock(prod_mutex_);
not_empty_.wait(lock, [this] {return !prod_queue_.empty() || nonblock_; });
std::swap(prod_queue_, cons_queue_);
return cons_queue_.size();
}
bool nonblock_;
std::queue<T> prod_queue_;
std::queue<T> cons_queue_;
std::mutex prod_mutex_;
std::mutex cons_mutex_;
std::condition_variable not_empty_;
};