前言
在后端高并发开发中,线程池 是绕不开的基础设施。频繁手动创建、销毁线程会带来巨额的内核上下文切换开销、系统资源占用,同时无限制创建线程极易导致服务 OOM、崩溃。
FixedThreadPool(固定线程池)是所有线程池中最基础、最稳定、工业界使用最广泛 的线程池模型:线程数量在创建时固定,全程不再动态增减,配合阻塞同步队列做任务排队,完美实现线程复用、并发限流、任务调度、优雅退出。
一、FixedThreadPool 核心概述
1.1 线程池基础需求
FixedThreadPool 定义:固定大小的线程池
- 线程池初始化时,一次性创建指定数量的工作线程,线程生命周期内数量固定不变
- 业务线程作为生产者提交任务,存入阻塞任务队列
- 预先创建的工作线程作为消费者,循环从队列中取出任务并行执行
- 所有线程执行完任务后不会销毁,归还线程池等待下一个任务,实现线程复用
- 队列满时触发拒绝策略,保护系统内存不暴涨
1.2 经典三层架构:半同步 / 半异步模式
你实现的线程池严格遵循工业标准三层解耦架构 ,本质是经典生产者 - 消费者模型:
-
**同步服务层(生产者)**外部业务线程,负责提交任务,将任务写入同步队列,无需关心任务执行细节,主线程不阻塞。
-
排队层(SyncQueue 同步队列) 整个线程池的核心中间层。负责多线程安全、任务缓存、队列限流、线程同步阻塞,上下两层通信的桥梁,限制任务上限避免内存溢出。
-
**异步服务层(消费者)**预先创建好的固定数量工作线程,循环从同步队列中取出任务,异步并行执行。
1.3 核心技术栈
本次实现全部基于 C++11 原生标准库,无第三方依赖:
- 线程管理:
std::thread、std::shared_ptr智能指针- 线程同步:
std::mutex互斥锁、std::condition_variable条件变量- RAII 锁管理:
std::unique_lock- 原子操作:
std::atomic<bool>线程安全运行标记- 一次性执行:
std::once_flag、std::call_once- 任务封装:
std::function、std::packaged_task、std::future- 移动语义 & 完美转发:
std::move、std::forward,实现零拷贝任务传递
二、核心组件:SyncQueue 同步队列完整解析
同步队列是线程池的灵魂,所有的线程安全、阻塞通信、限流逻辑全部封装在此。
2.1 同步队列设计目标
- 多线程安全:多线程并发入队、出队无数据竞争
- 双向阻塞:队列满时阻塞生产者(任务提交线程);队列为空时阻塞消费者(工作线程)
- 队列上限限流:预设最大任务容量,防止任务无限堆积导致内存暴涨
- 高性能优化:批量移动取任务,减少加锁次数;移动语义避免数据拷贝
- 优雅停止:支持队列安全终止,唤醒所有阻塞线程,避免死锁
- 超时等待:新增超时阻塞机制,避免生产者无限死等
2.2 结构体成员详解
cpp
template <class T>
class SyncQueue
{
private:
std::deque<T> m_queue; // 底层容器:双端队列(存任务),头尾操作O(1)
std::mutex m_mutex; // 互斥锁,保护队列所有共享操作
std::condition_variable m_notEmpty; // 条件变量:队列非空,唤醒消费者线程
std::condition_variable m_notFull; // 条件变量:队列未满,唤醒生产者线程
std::condition_variable m_waitStop; // 条件变量:等待队列为空再停止
int m_maxSize; // 队列最大容量上限
bool m_needStop; // true停止标记(初始为false)
int m_waitTime = 100; // 超时等待时间,单位ms
2.3 核心函数逐行原理精讲
1. Add 函数(生产者入队核心)
cpp
template <class F>
int Add(F &&task)
{
std::unique_lock<std::mutex> locker(m_mutex);
// 带超时等待:队列未满 或 线程池停止 就解除阻塞
auto tag = m_notFull.wait_for(locker,
std::chrono::milliseconds(m_waitTime),
[this]() -> bool
{
return m_needStop || !IsFull();
});
if (m_needStop)
{
return 2; // 线程池已停止,入队失败
}
if (!tag) // 超时队列依旧满,触发拒绝策略
{
return 1; // IsFull() true;
}
// 完美转发,零拷贝将任务放入队列尾部
m_queue.push_back(std::forward<F>(task)); // 放入队列
m_notEmpty.notify_all(); // 唤醒消费者:有活干了!
return 0;
}
核心逻辑:
- 使用
wait_for超时等待,不会无限阻塞生产者,超时队列依旧满就直接返回失败,触发拒绝策略 - Lambda 谓词双判断:
线程池停止 || 队列未满,只要满足其一就唤醒 std::forward完美转发,保留参数左右值属性,杜绝不必要的数据拷贝- 入队完成后唤醒消费者线程,通知工作线程来取任务执行
2. Take 函数(消费者出队核心)
cpp
void Take(T &task)
{
std::unique_lock<std::mutex> locker(m_mutex); // 加锁
// 队列为空 且 线程池未停止 → 线程阻塞等待
while (!m_needStop && IsEmpty())
{
m_notEmpty.wait(locker); // 阻塞,等待不为空
}
if (m_needStop)
{
return;
}
task = m_queue.front();
m_queue.pop_front(); // 取出任务
m_notFull.notify_all(); // 唤醒生产者:有空位啦!
}
核心逻辑:
while循环判断防止虚假唤醒,是条件变量使用的黄金准则- 队列为空时,工作线程主动释放锁、进入阻塞休眠,不占用 CPU 资源
- 取出队首任务后,唤醒生产者线程,告知队列已有空余位置
3. Task 批量取任务(极致性能优化)
cpp
// 批量取任务(高性能)
void Task(std::deque<T> &tqu)
{
std::unique_lock<std::mutex> locker(m_mutex);
// 队列为空 且 线程池未停止 → 线程阻塞等待
while (!m_needStop && IsEmpty())
{
m_notEmpty.wait(locker);
}
if (m_needStop)
{
return;
}
tqu = std::move(m_queue); // 【一次性拿走所有任务】
m_notFull.notify_all();
}
优化原理:传统单条取任务,每拿一个任务就要加锁、解锁一次,频繁锁竞争严重损耗性能。
本实现一次加锁,直接把队列所有任务整块移动出去 ,加锁次数从O(n)降为O(1),配合std::move移动语义无内存拷贝,极大提升高并发下的任务处理吞吐量。
4. Stop & WaitQueueEmptyStop 优雅停止接口
cpp
// 立即停止:直接置位标记,唤醒所有线程
void Stop()
{
{
std::unique_lock<std::mutex> locker(m_mutex);
m_needStop = true;
}
// 锁外唤醒!性能优化点
m_notEmpty.notify_all();
m_notFull.notify_all();
}
// 等待队空停止:处理完所有任务再停止(业务最常用)
void WaitQueueEmptyStop()
{
std::unique_lock<std::mutex> locker(m_mutex);
// 等待队列所有任务消费完毕
while (!IsEmpty())
{
m_waitStop.wait_for(locker, std::chrono::milliseconds(m_waitTime));
}
m_needStop = true;
m_notFull.notify_all();
m_notEmpty.notify_all();
}
关键优化考点:notify_all 放在锁外 unique_lock 的析构会自动解锁。如果唤醒操作写在锁范围内,被唤醒的线程会先阻塞等待锁释放,造成不必要的等待;将唤醒操作放到锁作用域之外,锁提前释放,被唤醒的线程可以直接抢占锁,减少线程调度开销,提升性能。
2.4.SyncQueue 同步队列完整代码
cpp
#include <list>
#include <vector>
#include <deque>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <iostream>
using namespace std;
#include "Logger.hpp"
#define QUESTOP 2
#define QUEEMPTY 1
#define ok 0
#ifndef SYNC_QUEUE_1_HPP
#define SYNC_QUEUE_1_HPP
namespace tulun
{
static const size_t MaxTaskCount = 500;
template <class T> // Task = 队列里存的类型
class SyncQueue
{
private:
std::deque<T> m_queue; // 底层容器:双端队列(存任务),头尾操作O(1)
std::mutex m_mutex; // 互斥锁,保护队列所有共享操作
std::condition_variable m_notEmpty; // 条件变量:队列非空,唤醒消费者线程
std::condition_variable m_notFull; // 条件变量:队列未满,唤醒生产者线程
std::condition_variable m_waitStop; // 条件变量:等待队列为空再停止
int m_maxSize; // 队列最大容量上限
bool m_needStop; // true停止标记(初始为false)
int m_waitTime = 100; // 超时等待时间,单位ms
bool IsFull() const
{
bool full = m_queue.size() >= m_maxSize;
// LOG_INFO << "full: " << full;
// LOG_FATAL<<"full: "<<full;
return full;
}
bool IsEmpty() const
{
bool empty = m_queue.empty();
// LOG_INFO << "empty: " << empty;
return empty;
}
template <class F> // F 传进来的参数类型
int Add(F &&task)
{
std::unique_lock<std::mutex> locker(m_mutex); // 加锁
auto tag = m_notFull.wait_for(locker,
std::chrono::milliseconds(m_waitTime),
[this]() -> bool
{
return m_needStop || !IsFull();
});
if (m_needStop)
{
return 2; // 线程池已停止,入队失败
}
if (!tag) // 超时队列依旧满,触发拒绝策略
{
return 1; // IsFull() true;
}
// 完美转发,零拷贝将任务放入队列尾部
m_queue.push_back(std::forward<F>(task)); // 放入队列
m_notEmpty.notify_all(); // 唤醒消费者:有活干了!
return 0;
}
public:
SyncQueue(int maxsize = MaxTaskCount)
: m_maxSize(maxsize),
m_needStop(false)
{
}
~SyncQueue()
{
if (!m_needStop)
{
Stop();
}
}
SyncQueue(const SyncQueue &) = delete;
SyncQueue &operator=(const SyncQueue &) = delete;
// 生产者 Put(放任务)
int Put(const T &task)
{
return Add(task);
}
int Put(T &&task)
{
return Add(std::forward<T>(task));
}
// 消费者 Take(取任务)
void Take(T &task)
{
std::unique_lock<std::mutex> locker(m_mutex); // 加锁
// 队列为空 且 线程池未停止 → 线程阻塞等待
while (!m_needStop && IsEmpty())
{
m_notEmpty.wait(locker); // 阻塞,等待不为空
}
if (m_needStop)
{
return;
}
task = m_queue.front();
m_queue.pop_front(); // 取出任务
m_notFull.notify_all(); // 唤醒生产者:有空位啦!
}
// 批量取任务(高性能)
void Task(std::deque<T> &tqu)
{
std::unique_lock<std::mutex> locker(m_mutex);
// 队列为空 且 线程池未停止 → 线程阻塞等待
while (!m_needStop && IsEmpty())
{
m_notEmpty.wait(locker);
}
if (m_needStop)
{
return;
}
tqu = std::move(m_queue); // 【一次性拿走所有任务】
m_notFull.notify_all();
}
// 立即停止:直接置位标记,唤醒所有线程
void Stop()
{
{
std::unique_lock<std::mutex> locker(m_mutex);
m_needStop = true;
}
// 锁外唤醒!性能优化点
m_notEmpty.notify_all();
m_notFull.notify_all();
}
// 等待队空停止:处理完所有任务再停止(业务最常用)
void WaitQueueEmptyStop()
{
std::unique_lock<std::mutex> locker(m_mutex);
// 等待队列所有任务消费完毕
while (!IsEmpty())
{
m_waitStop.wait_for(locker, std::chrono::milliseconds(m_waitTime));
}
m_needStop = true;
m_notFull.notify_all();
m_notEmpty.notify_all();
}
bool Empty() const
{
std::unique_lock<std::mutex> locker(m_mutex);
return m_queue.empty();
}
bool Full() const
{
std::unique_lock<std::mutex> locker(m_mutex);
return m_queue.size() >= m_maxSize;
}
size_t Size() const
{
std::unique_lock<std::mutex> locker(m_mutex);
return m_queue.size();
}
size_t Count() const
{
return m_queue.size();
}
};
} // namespace tulun
#endif
三、FixedThreadPool 线程池整体实现解析
3.1 线程池成员变量
cpp
class FixedThreadPool
{
public:
using TaskType = std::function<void(void)>;
private:
std::list<std::shared_ptr<std::thread>> m_threadgroup; // 工作线程组
tulun::SyncQueue<TaskType> m_queue; // 同步阻塞任务队列
std::atomic<bool> m_running; // 原子运行标记,线程安全无锁
std::once_flag m_flag; // 保证停止逻辑只执行一次
std::shared_ptr<std::thread>:智能指针管理线程生命周期,自动回收资源,避免内存泄漏std::atomic<bool>:原子变量,多线程读写无数据竞争,无需加锁保护std::once_flag:配合std::call_once,保证线程池优雅停止只会执行一次,防止重复 join 线程崩溃
3.2 核心生命周期函数
1. 构造函数 & Start 线程启动
cpp
FixedThreadPool::FixedThreadPool(size_t m_TaskQueSize, int numthreads)
: m_queue(m_TaskQueSize), m_running(false)
{
Start(numthreads); // 构造函数自动启动线程池
}
void FixedThreadPool::Start(int numthreads)
{
m_running = true;
// 一次性创建指定数量的固定工作线程
for (int i = 0; i < numthreads; ++i)
{
m_threadgroup.push_back(
std::shared_ptr<std::thread>(
new std::thread(&FixedThreadPool::RunInThread, this)));
}
}
默认线程数量:std::thread::hardware_concurrency(),自动获取 CPU 核心数,是 CPU 密集任务最优线程数配置。
2. RunInThread 工作线程主循环
cpp
void FixedThreadPool::RunInThread()
{
while (m_running)
{
TaskType task;
m_queue.Take(task); // 空队列自动阻塞
if (m_running && task)
{
LOG_INFO << "Thread task";
task(); // 执行任务
}
}
}
工作线程死循环逻辑:取任务 → 执行任务 。线程池运行时,所有工作线程全部阻塞在同步队列的Take接口,无任务时休眠,不占用 CPU 资源;有任务时被唤醒执行,执行完继续循环等待。
3. Stop 优雅停止体系
cpp
FixedThreadPool::~FixedThreadPool()
{
Stop(); // 析构函数自动停止线程池,RAII安全回收
}
void FixedThreadPool::Stop()
{
// 保证全局只停止一次
std::call_once(m_flag, &FixedThreadPool::StopThreadGroup, this);
}
void FixedThreadPool::StopThreadGroup()
{
m_queue.WaitQueueEmptyStop(); // 先等待队列所有任务执行完毕
m_running = false; // 关闭线程运行标记
// 等待所有工作线程正常退出,回收线程资源
for (auto &tha : m_threadgroup)
{
tha->join();
}
}
完整优雅停止流程:
- 触发
WaitQueueEmptyStop,等待队列中所有遗留任务全部消费完成 - 设置运行标记为
false,唤醒所有阻塞的工作线程 - 工作线程跳出循环,主线程
join回收所有线程 - 全程无任务丢失、无内存泄漏、无线程死锁
3.3 任务提交接口
普通无返回值任务:AddTask
重载左右值两个接口,兼容所有可调用对象:
cpp
void FixedThreadPool::AddTask(TaskType &&task)
{
// 队列满超时,触发调用者运行拒绝策略
if (m_queue.Put(std::forward<TaskType>(task)) != 0)
{
LOG_INFO << "task()";
task();
}
}
void FixedThreadPool::AddTask(const TaskType &task)
{
if (m_queue.Put(task) != 0)
{
LOG_INFO << "task()";
task();
}
}
带返回值万能 submit 接口(高级封装)
支持任意普通函数、类静态成员函数、类普通成员函数、任意参数、任意返回值 ,通过packaged_task封装 +future获取返回值,完美适配业务需求。
cpp
template <class Func, class... Args>
auto submit(Func &&func, Args &&...args)
{
// 万能返回值推导,支持成员函数
using RetType = decltype(std::invoke(std::forward<Func>(func), std::forward<Args>(args)...));
// 捕获所有参数,封装任务
auto task = std::make_shared<std::packaged_task<RetType()>>(
[func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable
{
return std::invoke(func, args...);
});
std::future<RetType> result = task->get_future();
// 队列满触发拒绝策略:直接在调用线程执行任务
if (m_queue.Put([task]() -> void { (*task)(); }) != 0)
{
LOG_ERROR << "任务队列已满,触发调用者运行拒绝策略";
(*task)();
}
return result;
}
3.4 FixedThreadPool完整代码
FixedThreadPool.hpp
cpp
#include "SyncQueue_1.hpp"
#include <functional>
#include <thread>
#include <vector>
#include <queue>
#include <list>
#include <memory>
#include <atomic>
#include <future>
using namespace std;
#ifndef FIXED_THREAD_POOL_HPP
#define FIXED_THREAD_POOL_HPP
namespace tulun
{
class FixedThreadPool
{
public:
using TaskType = std::function<void(void)>; // std::bind;'
private:
std::list<std::shared_ptr<std::thread>> m_threadgroup; // 工作线程组
tulun::SyncQueue<TaskType> m_queue; // 同步阻塞任务队列
std::atomic<bool> m_running; // 原子运行标记,线程安全无锁
std::once_flag m_flag; // 保证停止逻辑只执行一次
void Start(int numthreads);
void RunInThread();
void StopThreadGroup();
public:
FixedThreadPool(size_t m_TaskQueSize = 500,
int numthreads = std::thread::hardware_concurrency());
~FixedThreadPool();
void Stop();
void AddTask(TaskType &&task);
void AddTask(const TaskType &task);
template <class Func, class... Args>
auto submit(Func &&func, Args &&...args)
{
// typedef decltype(func(args...)) RetType;
// using RetType = decltype(func(args...));
// std::packaged_task<RetType()> task(
// std::bind(
// std::forward<Func>(func),
// std::forward<Args>(args)...)
//);
// 正确推导返回值(invoke:支持成员函数)
using RetType = decltype(std::invoke(std::forward<Func>(func), std::forward<Args>(args)...));
// 封装任务
auto task = std::make_shared<std::packaged_task<RetType()>>(
[func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable
{
return std::invoke(func, args...);
});
std::future<RetType> result = task->get_future();
// 队列满触发拒绝策略:直接在调用线程执行任务
if (m_queue.Put([task]() -> void
{ (*task)(); }) != 0)
{
LOG_ERROR << "Add task run task";
(*task)();
}
return result;
}
};
} // namespace tulun
#endif
FixedThreadPool.cpp
cpp
#include "FixedThreadPool.hpp"
namespace tulun
{
// class FixedThreadPool
// using TaskType = std::funcation<void(void)>; // std::bind;'
// std::list<std::shared_ptr<std::thread>> m_threadgroup;
// tulun::SyncQueue<TaskType> m_queue;
// std::atomic<bool> m_running;
// std::once_flag m_flag;
void FixedThreadPool::Start(int numthreads)
{
m_running = true;
// 一次性创建指定数量的固定工作线程
for (int i = 0; i < numthreads; ++i)
{
// std::shared_ptr<std::thread> tha(
// new std::thread(&FixedThreadPool::RunInThread,this));
m_threadgroup.push_back(
std::shared_ptr<std::thread>(
new std::thread(&FixedThreadPool::RunInThread, this)));
// m_threadgroup.push_back(
// std::make_shared<std::thread>(
// &FixedThreadPool::RunInThread, this));
}
}
void FixedThreadPool::RunInThread()
{
while (m_running)
{
TaskType task;
m_queue.Take(task); // 空队列自动阻塞
if (m_running && task)
{
LOG_INFO << "Thread task";
task(); // 执行任务
}
}
}
// 等待所有线程结束
void FixedThreadPool::StopThreadGroup()
{
// m_queue.Stop();
m_queue.WaitQueueEmptyStop(); // 先等待队列所有任务执行完毕
m_running = false; // 关闭线程运行标记
// 等待所有工作线程正常退出,回收线程资源
for (auto &tha : m_threadgroup)
{
tha->join();
}
}
FixedThreadPool::FixedThreadPool(size_t m_TaskQueSize, int numthreads)
: m_queue(m_TaskQueSize),
m_running(false)
{
Start(numthreads); // 构造函数自动启动线程池
}
FixedThreadPool::~FixedThreadPool()
{
Stop(); // 析构函数自动停止线程池,RAII安全回收
}
void FixedThreadPool::Stop()
{
// 保证全局只停止一次
std::call_once(m_flag, &FixedThreadPool::StopThreadGroup, this);
}
void FixedThreadPool::AddTask(TaskType &&task)
{
// 队列满超时,触发调用者运行拒绝策略
if (m_queue.Put(std::forward<TaskType>(task)) != 0)
{
LOG_INFO << "task()";
task();
}
}
void FixedThreadPool::AddTask(const TaskType &task)
{
if (m_queue.Put(task) != 0)
{
LOG_INFO << "task()";
task();
}
}
} // namespace tulun
四、线程池四大拒绝策略全解析
当线程池所有工作线程满载 + 任务队列已满,新任务无法入队时,就会触发拒绝策略,保护系统不被无限任务压垮。
- AbortPolicy 中止策略默认策略,队列满直接抛出异常,调用者捕获异常自行处理。适合对任务可靠性要求高、不允许任务丢失的业务。
- DiscardPolicy 丢弃策略静默丢弃新任务,不报错、不处理。适合容忍任务丢失、高吞吐非核心日志类任务。
- DiscardOldestPolicy 抛弃最老策略 丢弃队列头部最早等待的任务,腾出位置存入新任务。不可搭配优先级队列使用,会误丢高优先级任务。
- CallerRunsPolicy 调用者运行策略(你代码实现的方案) 最安全、工程最常用策略 :队列满时,不在线程池执行,直接在提交任务的业务主线程中执行该任务 。
- 不会丢失任务、不会抛异常
- 自带流量反压:主线程被任务阻塞,暂停新任务提交,给线程池消化任务的时间
- 完美适配后端服务器接口场景,你全部
AddTask、submit接口均默认实现此策略。
4.1 改写 AddTask 接口的底层原理
你源码中对AddTask的改造,核心就是实现调用者运行拒绝策略:
cpp
// Put返回非0 = 队列满、入队失败
if (m_queue.Put(...) != 0)
{
task(); // 直接在调用线程执行任务
}
通过同步队列的超时等待返回值,判断队列状态,满则拒绝入队,回退给调用线程执行,实现系统流量保护。
五、完整测试用例运行讲解
cpp
void funa() { cout << "funa " << endl; }
void funb(int a) { cout << "funb : a " << a << endl; }
int Add(int a, int b) { return a + b; }
struct Bar
{
static void func(int a) { cout << "Bar: a " << a << endl; }
};
class Object
{
private:
int value;
public:
Object(int x = 0) : value(x) {}
void Print() const { cout << "Object::Print: value " << value << endl; }
int Add(int a,int b) { value = a+b; return a+b; }
};
int main()
{
tulun::Logger::setLogLevel(tulun::LOG_LEVEL::TRACE);
// 构造:队列最大500,线程数8
tulun::FixedThreadPool mypool(500, 8);
// 无参普通函数
mypool.AddTask(funa);
// 带参函数:bind绑定参数
mypool.AddTask(std::bind(funb, 10));
// submit 带返回值任务,future获取结果
auto fut = mypool.submit(Add, 12, 23);
LOG_INFO << "fut.get: " << fut.get(); // 输出 35
// 类静态成员函数
mypool.AddTask(std::bind(&Bar::func, 12));
// 普通类成员函数
Object objx(10);
mypool.AddTask(std::bind(&Object::Print,&objx));
// 带返回值类成员函数
auto fut2 = mypool.submit(&Object::Add,&objx,12,23);
LOG_INFO<<"fut2.get:"<<fut2.get(); // 输出 35
return 0;
}
六、FixedThreadPool 适用场景与注意事项
6.1 最优使用场景
- 并发限流场景:需要严格控制系统并发线程数,避免资源抢占、上下文切换过载
- 任务量稳定、短耗时任务:任务执行时长均匀,线程利用率稳定
- 后端服务器请求处理:Web 服务、网关接口、常规业务异步处理
- 批量任务并行处理:批量文件处理、批量数据计算、批量 IO 任务
6.2 注意事项 & 选型禁忌
- 线程数固定,不适合任务量忽大忽小、长耗时阻塞任务
- 队列满会触发拒绝策略,必须根据业务合理配置队列上限、线程数量
- 线程数配置原则:CPU 密集型任务 = CPU 核心数;IO 密集型任务可适当放大
- 禁止无限制扩大队列长度,否则会导致内存溢出 OOM
七、 高频考点总结
- 固定线程池三层架构是什么?同步服务层、排队层、异步服务层,生产者消费者模型。
- 条件变量为什么要用
while循环判断,不用if?防止操作系统虚假唤醒,唤醒后需要重新校验条件。 - **
notify_all为什么放在锁作用域之外?**减少线程等待锁的开销,提升唤醒性能。 - 线程池四大拒绝策略?你实现的是哪一种? 中止、丢弃、弃旧、调用者运行;本项目实现调用者运行策略。
- **为什么要用
std::atomic做运行标记?**原子变量无锁、多线程读写安全,不需要互斥锁保护。 - **批量取任务的优化原理?**一次加锁整块移动队列,大幅降低加锁次数,消除频繁锁竞争。
- **优雅停止的完整流程?**等待队列任务执行完毕 → 关闭运行标记 → 唤醒所有线程 → join 回收所有线程。