前言
在多线程编程中,线程池是解决 "线程创建销毁开销大""资源占用失控" 的核心方案。普通线程池往往存在 "线程数固定导致并发不足" 或 "空闲线程过多浪费资源" 的问题,本文实现的高可用线程池通过「核心线程常驻 + 非核心线程动态伸缩」+「任务超时监控」+「队列限流」等特性,兼顾了并发性能与资源利用率,适用于高并发、高可用的业务场景。
本文将从设计思路→核心特性→完整代码→关键逻辑解析→使用示例→注意事项逐步展开,代码可直接复用,同时附带详细注释和原理讲解,帮助大家理解线程池的核心设计。
一、设计思路
线程池的核心目标是 "高效管理线程资源,合理调度任务执行",本次设计围绕以下 3 个核心思路展开:
- 线程分层管理:核心线程(常驻不退出)保证基础并发能力,非核心线程(空闲超时销毁)应对突发任务峰值,实现资源动态伸缩;
- 任务安全调度:通过互斥锁 + 条件变量保证任务队列的线程安全访问,避免竞态条件;
- 高可用防护:任务队列限流防止内存溢出,任务超时监控便于问题排查,优雅停机保证资源不泄漏。
二、核心特性
- 动态扩缩容 :核心线程常驻,非核心线程空闲超时(默认 3 秒)自动销毁,任务峰值时最多扩容至
CPU核心数×2(可配置); - 队列限流:任务队列最大容量可配置(默认 500),队列满时拒绝新任务,避免内存无限增长;
- 任务超时监控:记录每个任务执行耗时,超时(默认 10 秒)打印告警日志(支持扩展强制终止逻辑);
- 线程安全:原子变量 + 互斥锁 + 条件变量三重保障,避免竞态条件和数据竞争;
- 优雅停机:停止时等待所有任务执行完毕,回收所有线程资源,无僵尸线程和资源泄漏;
- 状态查询:支持查询任务队列大小、活跃线程数、总线程数、核心线程数等状态。
三、完整实现代码
头文件(HighAvailabilityThreadPool.h)
cpp
#ifndef HIGHAVAILABILITYTHREADPOOL_H
#define HIGHAVAILABILITYTHREADPOOL_H
#include <unordered_map>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <atomic>
#include <chrono>
#include <stdexcept>
#include <sstream>
#include <memory>
#include "GlobalLogger.h" // 日志库(可替换为自己的日志实现)
// 定义任务类型:无参可调用对象(线程池队列存储的统一任务格式)
using Task = std::function<void()>;
/**
* @brief 线程数据结构体(存储单个工作线程的元信息)
* @note alignas(64):避免伪共享(CPU 缓存行优化,64字节为常见缓存行大小)
* 多个原子变量同属一个缓存行时,可能导致CPU缓存频繁失效,影响性能
*/
struct alignas(64) ThreadData
{
std::thread thread; // 工作线程实例(右值语义,禁止拷贝)
std::atomic<bool> IsCore; // 是否为核心线程(核心线程常驻,非核心线程空闲超时销毁)
std::atomic<bool> IsBusy{false}; // 线程是否正在执行任务(原子变量保证线程安全)
std::atomic<bool> IsAlive{true}; // 线程是否存活(标记线程是否需要退出)
// 构造函数:接收右值线程实例和核心线程标记
ThreadData(std::thread &&t, bool core);
// 移动构造函数(线程不可拷贝,仅支持移动)
ThreadData(ThreadData &&other) noexcept;
ThreadData(const ThreadData &) = delete; // 禁用拷贝构造(线程资源不可复制)
ThreadData &operator=(const ThreadData &) = delete; // 禁用拷贝赋值
ThreadData &operator=(ThreadData &&other) noexcept; // 移动赋值运算符
};
/**
* @brief 高可用线程池类(支持核心/非核心线程、任务超时、队列限流、自动清理空闲线程)
* @details 核心特性:
* 1. 核心线程常驻,非核心线程空闲超时自动销毁(资源动态伸缩)
* 2. 任务队列限流,避免内存溢出
* 3. 任务执行超时监控(仅告警,如需强制终止需扩展线程取消逻辑)
* 4. 线程安全的状态管理(原子变量+互斥锁+条件变量同步)
* 5. 优雅停机(等待所有任务执行完毕,回收所有线程资源)
*/
class HighAvailabilityThreadPool
{
public:
/**
* @brief 构造函数(初始化线程池核心参数)
* @param MinThreadNum 核心线程数(常驻线程,最小并发数,默认4)
* @param MaxThreadNum 最大线程数(线程池上限,默认CPU核心数×2,平衡并发与资源占用)
* @param IdleTimeoutMs 非核心线程空闲超时(单位:毫秒,默认3000ms)
* @param TaskTimeoutMs 单个任务执行超时阈值(单位:毫秒,默认10000ms)
* @param MaxQueueSize 任务队列最大容量(限流,默认500,避免队列无限增长)
* @throw std::invalid_argument 非法参数时抛出异常(如核心线程数为0)
*/
explicit HighAvailabilityThreadPool(
size_t MinThreadNum = 4,
size_t MaxThreadNum = std::thread::hardware_concurrency() * 2,
size_t IdleTimeoutMs = 3000,
size_t TaskTimeoutMs = 10000,
size_t MaxQueueSize = 500
);
/**
* @brief 析构函数(自动调用stop(),保证资源安全回收)
*/
~HighAvailabilityThreadPool();
// 禁用拷贝构造和赋值(线程池为资源管理对象,不可复制)
HighAvailabilityThreadPool(const HighAvailabilityThreadPool &) = delete;
HighAvailabilityThreadPool &operator=(const HighAvailabilityThreadPool &) = delete;
/**
* @brief 提交任务到线程池
* @tparam F 任务函数/可调用对象类型(万能引用,支持左值/右值)
* @tparam Args 任务参数类型包(可变参数)
* @param f 任务函数/可调用对象
* @param args 任务所需参数(完美转发,避免不必要拷贝)
* @return bool 任务提交成功返回true,失败(线程池停止/队列满)返回false
* @note 核心逻辑:将"函数+参数"绑定为无参Task,封装超时控制后入队,唤醒工作线程
*/
template <typename F, typename... Args>
bool submit(F &&f, Args &&...args /*参数包展开*/)
{
// 检查线程池是否已停止(原子变量load,线程安全)
if (IsStop_.load())
{
SPDLOG_ERROR("ThreadPool is stopped: cannot submit new task");
return false;
}
// 操作任务队列需加锁(TaskMutex_保护tasks_的线程安全访问)
{
std::lock_guard<std::mutex> lock(TaskMutex_);
// 任务队列满,拒绝新任务(限流机制)
if (tasks_.size() >= MaxQueueSize_)
{
SPDLOG_WARN("Task queue full (size: {}), reject new task", tasks_.size());
return false;
}
/**
* 第一步:用std::bind绑定"函数+参数",生成无参可调用对象
* - std::forward<F>(f):完美转发任务函数,保持其左值/右值属性(避免拷贝)
* - std::forward<Args>(args)...:完美转发参数包(参数可能多个,需展开)
* - 最终生成的task:调用task()等价于调用f(args...)
*/
auto task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
/**
* 第二步:二次封装task,添加超时控制逻辑
* - 捕获this:用于调用类成员函数ExecuteTaskWithTimeout
* - 按值捕获task:保证task生命周期与lambda一致(避免悬垂引用)
* 【避坑】不可按引用捕获&task:submit函数结束后task局部变量销毁,引用失效
* - 最终生成的WrappedTask:无参,调用时执行"超时监控+原始任务"
*/
auto WrappedTask = [this, task]()
{
ExecuteTaskWithTimeout(task); // 执行任务并监控超时
};
// 任务入队(std::move转移所有权,避免WrappedTask拷贝)
tasks_.emplace(std::move(WrappedTask));
}
// 检查是否需要创建新的非核心线程(动态扩缩容逻辑)
{
std::lock_guard<std::mutex> lock(ThreadMutex_);
// 条件:当前线程数<最大线程数,且等待任务数>活跃线程数(有任务没人做)
if (ThreadMap_.size() < MaxThreadNum_ && tasks_.size() > ActiveThreadNum_.load())
{
// 创建新工作线程,入口函数为worker成员函数(this为类实例指针)
std::thread t(&HighAvailabilityThreadPool::worker, this);
std::thread::id tid = t.get_id(); // 获取新线程ID
// 线程存入ThreadMap_管理(ThreadData接管线程所有权,标记为非核心线程)
ThreadMap_.emplace(tid, ThreadData(std::move(t), false));
TotalThreadNum_.store(ThreadMap_.size()); // 更新总线程数(原子变量,线程安全)
}
}
// 唤醒一个等待中的工作线程(避免惊群效应,用notify_one而非notify_all)
TaskCv_.notify_one();
return true;
}
/**
* @brief 停止线程池(优雅停机)
* @note 逻辑:标记停止状态→唤醒所有工作线程→等待清理线程退出→等待所有工作线程销毁
*/
void stop();
/**
* @brief 获取当前任务队列大小(线程安全)
* @return size_t 任务队列中等待执行的任务数
*/
size_t GetTaskQueueSize() const;
/**
* @brief 获取当前活跃线程数(正在执行任务的线程数)
* @return size_t 活跃线程数(原子变量直接load,无锁高效)
*/
size_t GetActiveThreadCount() const;
/**
* @brief 获取当前总线程数(核心+非核心)
* @return size_t 总线程数(原子变量,线程安全)
*/
size_t GetTotalThreadCount() const;
/**
* @brief 获取当前核心线程数
* @return size_t 核心线程数(需遍历ThreadMap_,加锁保证线程安全)
*/
size_t GetCoreThreadCount() const;
private:
/**
* @brief 工作线程入口函数(每个工作线程独立执行)
* @note 核心逻辑:循环等待任务→获取任务→执行任务→非核心线程空闲超时退出
*/
void worker();
/**
* @brief 执行任务并监控超时
* @param task 待执行的无参任务
* @note 逻辑:记录开始时间→执行任务→计算耗时→超时则打告警日志(仅监控,不强制终止)
*/
void ExecuteTaskWithTimeout(const Task &task);
/**
* @brief 清理线程(独立线程,定期回收已退出的工作线程)
* @note 逻辑:每1秒检查一次ThreadMap_,销毁IsAlive为false的线程(join+erase)
* 作用:回收线程资源,避免僵尸线程和内存泄漏
*/
void CleanUpDeadThreads();
// 线程管理容器:key=线程ID,value=线程元信息(ThreadData)
std::unordered_map<std::thread::id, ThreadData> ThreadMap_;
// 任务队列:存储待执行的无参任务(std::queue先进先出)
std::queue<Task> tasks_;
mutable std::mutex TaskMutex_; // 保护任务队列tasks_的互斥锁(mutable允许const函数加锁)
mutable std::mutex ThreadMutex_; // 保护线程容器ThreadMap_的互斥锁
std::condition_variable TaskCv_; // 任务条件变量(工作线程等待/唤醒)
std::condition_variable ThreadExitCv_; // 线程退出条件变量(主线程等待所有工作线程退出)
const size_t MinThreadNum_; // 核心线程数(常量,初始化后不可修改)
const size_t MaxThreadNum_; // 最大线程数(常量)
const size_t IdleTimeoutMs_; // 非核心线程空闲超时(常量)
const size_t TaskTimeoutMs_; // 任务执行超时阈值(常量)
const size_t MaxQueueSize_; // 任务队列最大容量(常量)
std::atomic<bool> IsStop_; // 线程池停止标记(原子变量,线程安全)
std::atomic<size_t> ActiveThreadNum_; // 活跃线程数(正在执行任务的线程数)
std::atomic<size_t> TotalThreadNum_; // 总线程数(核心+非核心)
std::thread CleanUpThread_; // 清理线程(独立线程,定期回收死线程)
std::atomic<bool> CleanUpStop_; // 清理线程停止标记(原子变量)
};
#endif // HIGHAVAILABILITYTHREADPOOL_H
源文件(HighAvailabilityThreadPool.cpp)
cpp
#include "HighAvailabilityThreadPool.h"
/**
* @brief ThreadData构造函数(接管线程所有权)
* @param t 右值线程实例(std::thread不可拷贝,只能移动)
* @param core 是否为核心线程
*/
ThreadData::ThreadData(std::thread &&t/*右值引用,接收移动语义*/, bool core)
{
thread = std::move(t); // 移动线程实例(不拷贝,仅转移所有权)
IsCore = core; // 初始化核心线程标记
}
/**
* @brief ThreadData移动构造函数
* @param other 待移动的ThreadData实例
* @note noexcept:声明不抛出异常,让编译器优化移动逻辑(避免额外开销)
*/
ThreadData::ThreadData(ThreadData &&other) noexcept
{
thread = std::move(other.thread); // 移动线程
IsCore = other.IsCore.load(); // 原子变量load(线程安全读取)
IsBusy = other.IsBusy.load();
IsAlive = other.IsAlive.load();
}
/**
* @brief ThreadData移动赋值运算符
* @param other 待移动的ThreadData实例
* @return ThreadData& 移动后的当前实例引用
* @note 1. 先判断自我赋值(避免重复操作)
* 2. 原子变量用store存储(线程安全写入)
*/
ThreadData &ThreadData::operator=(ThreadData &&other) noexcept
{
if (this != &other)/*避免自我赋值(a = std::move(a))*/
{
thread = std::move(other.thread);
IsCore.store(other.IsCore.load()); // 原子变量线程安全赋值
IsBusy.store(other.IsBusy.load());
IsAlive.store(other.IsAlive.load());
}
return *this;
}
/**
* @brief 线程池构造函数实现(初始化核心线程和清理线程)
*/
HighAvailabilityThreadPool::HighAvailabilityThreadPool(
size_t MinThreadNum, size_t MaxThreadNum,
size_t IdleTimeoutMs, size_t TaskTimeoutMs, size_t MaxQueueSize
) : MinThreadNum_(MinThreadNum), MaxThreadNum_(MaxThreadNum),
IdleTimeoutMs_(IdleTimeoutMs), TaskTimeoutMs_(TaskTimeoutMs),
MaxQueueSize_(MaxQueueSize)
{
IsStop_ = false; // 初始化线程池为运行状态
ActiveThreadNum_ = 0; // 初始活跃线程数为0
TotalThreadNum_ = 0; // 初始总线程数为0
// 参数合法性校验(非法参数直接抛出异常,避免后续逻辑出错)
if (MinThreadNum_ == 0)
{
throw std::invalid_argument("MinThreadNum cannot be 0");
}
if (MaxThreadNum_ < MinThreadNum_)
{
throw std::invalid_argument("MaxThreadNum cannot be less than MinThreadNum");
}
if (MaxQueueSize_ == 0)
{
throw std::invalid_argument("MaxQueueSize cannot be 0");
}
// 创建核心线程(加锁保护ThreadMap_)
{
std::lock_guard<std::mutex> lock(ThreadMutex_);
for (size_t i = 0; i < MinThreadNum_; ++i)
{
// 核心线程入口函数为worker,标记为核心线程(core=true)
std::thread t(&HighAvailabilityThreadPool::worker, this);
std::thread::id tid = t.get_id();
ThreadMap_.emplace(tid, ThreadData(std::move(t), true));
}
TotalThreadNum_.store(ThreadMap_.size()); // 更新总线程数(核心线程数)
}
// 创建清理线程(独立线程,定期清理死线程)
CleanUpThread_ = std::thread(&HighAvailabilityThreadPool::CleanUpDeadThreads, this);
}
/**
* @brief 析构函数(自动停止线程池,保证资源回收)
*/
HighAvailabilityThreadPool::~HighAvailabilityThreadPool()
{
stop(); // 调用stop()优雅停机
}
/**
* @brief 停止线程池实现(优雅停机)
*/
void HighAvailabilityThreadPool::stop()
{
// 双重检查:避免重复调用stop()
if (IsStop_.load())
{
return;
}
// 标记线程池为停止状态(加锁保护,确保与submit()的IsStop_检查互斥)
{
std::lock_guard<std::mutex> lock(TaskMutex_);
IsStop_.store(true);
}
// 唤醒所有等待任务的工作线程(让它们检查IsStop_并退出)
TaskCv_.notify_all();
// 停止清理线程(标记清理线程退出)
CleanUpStop_.store(true);
// 等待清理线程执行完毕(join回收资源)
if (CleanUpThread_.joinable())
{
CleanUpThread_.join();
}
// 等待所有工作线程退出(ThreadExitCv_等待ThreadMap_为空)
{
std::unique_lock<std::mutex> ExitLock(ThreadMutex_);
ThreadExitCv_.wait(ExitLock, [this]()
{ return ThreadMap_.empty(); });
}
}
/**
* @brief 工作线程入口函数实现
*/
void HighAvailabilityThreadPool::worker()
{
const std::thread::id tid = std::this_thread::get_id(); // 获取当前线程ID
std::stringstream ss;
ss << tid;
const std::string ThreadIdStr = ss.str(); // 线程ID转为字符串(日志用)
ThreadData *CurrentThreadData = nullptr;
// 从ThreadMap_中查找当前线程的元信息(加锁保护)
{
std::lock_guard<std::mutex> lock(ThreadMutex_);
auto it = ThreadMap_.find(tid);
if (it == ThreadMap_.end())
{
SPDLOG_ERROR("Thread [{}] not found in ThreadMap_, exit", ThreadIdStr);
return;
}
CurrentThreadData = &(it->second); // 拿到当前线程的元信息指针
}
const bool IsCoreThread = CurrentThreadData->IsCore.load(); // 是否为核心线程(一次读取,后续不变)
// 线程主循环:未停止则持续运行
while (!IsStop_.load())
{
Task task; // 存储待执行的任务
bool HasTask = false; // 是否成功获取任务
// 加锁获取任务(std::unique_lock支持wait()自动解锁/上锁)
{
std::unique_lock<std::mutex> lock(TaskMutex_); // 必须用unique_lock(wait()需要手动解锁)
if (IsCoreThread)
{
/**
* 核心线程:永久等待(直到有任务或线程池停止)
* - wait()逻辑:解锁→阻塞→被唤醒后重新上锁→检查条件
* - 条件:线程池停止 或 任务队列非空(避免虚假唤醒)
* - 核心线程不会因空闲退出,保证常驻
*/
TaskCv_.wait(lock, [this]()
{ return IsStop_.load() || !tasks_.empty(); });
}
else
{
/**
* 非核心线程:超时等待(空闲超时时长后退出)
* - wait_for():阻塞指定时间,超时后自动返回
* - 返回值HasTask:true=被唤醒且条件满足,false=超时
* - 目的:避免非核心线程长期空闲占用资源
*/
const auto timeout = std::chrono::milliseconds(IdleTimeoutMs_);
HasTask = TaskCv_.wait_for(lock, timeout, [this]()
{ return IsStop_.load() || !tasks_.empty(); });
}
// 线程池未停止且队列有任务:取出任务(移动语义,避免拷贝)
if (!IsStop_.load() && !tasks_.empty())
{
task = std::move(tasks_.front()); // 转移任务所有权
tasks_.pop(); // 从队列移除任务
HasTask = true; // 标记成功获取任务
}
} // 解锁:任务获取后释放锁,让其他线程能操作队列
// 成功获取任务:执行任务(带状态更新和异常捕获)
if (HasTask && task)
{
CurrentThreadData->IsBusy.store(true); // 标记线程为忙碌状态
ActiveThreadNum_.fetch_add(1); // 活跃线程数+1(原子操作,线程安全)
try
{
task(); // 执行任务(调用WrappedTask,内含超时监控)
}
catch (const std::exception &e)
{
// 捕获标准异常,避免线程因异常退出
SPDLOG_ERROR("Thread [{}] task failed: {}", ThreadIdStr, e.what());
}
catch (...)
{
// 捕获未知异常,保证线程稳定性
SPDLOG_ERROR("Thread [{}] task failed with unknown exception", ThreadIdStr);
}
ActiveThreadNum_.fetch_sub(1); // 活跃线程数-1
CurrentThreadData->IsBusy.store(false); // 标记线程为空闲状态
}
/**
* 非核心线程退出条件:未获取任务 + 线程池未停止
* - 说明:非核心线程超时未拿到任务,触发逻辑退出(break跳出主循环)
* - 注意:此处仅为逻辑退出,线程资源需由CleanUpDeadThreads回收
*/
if (!IsCoreThread && !HasTask && !IsStop_.load())
{
break;
}
}
// 线程退出前:标记为非存活,通知清理线程/主线程
CurrentThreadData->IsAlive.store(false); // 逻辑退出标记
ThreadExitCv_.notify_one(); // 唤醒等待线程退出的主线程
}
/**
* @brief 执行任务并监控超时实现
* @param task 待执行的无参任务(const引用,避免拷贝)
*/
void HighAvailabilityThreadPool::ExecuteTaskWithTimeout(const Task &task)
{
const auto StartTime = std::chrono::steady_clock::now(); // 记录任务开始时间
try
{
task(); // 执行原始任务(已绑定函数+参数的无参对象)
}
catch (const std::exception &e)
{
SPDLOG_ERROR("Task threw exception: {}", e.what());
return;
}
catch (...)
{
SPDLOG_ERROR("Task threw unknown exception");
return;
}
// 计算任务执行耗时(毫秒)
const auto DurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - StartTime
).count();
// 任务执行超时:打告警日志(仅监控,不强制终止任务)
if (DurationMs > TaskTimeoutMs_)
{
SPDLOG_WARN("Task timed out - {}ms (limit: {}ms)", DurationMs, TaskTimeoutMs_);
}
}
/**
* @brief 清理线程实现(定期回收死线程)
*/
void HighAvailabilityThreadPool::CleanUpDeadThreads()
{
// 清理线程主循环:未停止则持续运行
while (!CleanUpStop_.load())
{
std::this_thread::sleep_for(std::chrono::seconds(1)); // 每1秒检查一次(低开销)
// 加锁遍历ThreadMap_,清理非存活线程
std::lock_guard<std::mutex> lock(ThreadMutex_);
auto it = ThreadMap_.begin();
while (it != ThreadMap_.end())
{
ThreadData &data = it->second;
// 线程已标记为非存活(IsAlive=false):回收资源
if (!data.IsAlive.load())
{
std::stringstream ss;
ss << it->first;
const std::string ThreadIdStr = ss.str();
// 1. 回收系统线程资源:join() 等待线程执行完毕,释放CPU/内存资源
if (data.thread.joinable())
{
data.thread.join();
}
// 2. 从线程管理容器中删除:移除ThreadMap_中的无效线程数据
it = ThreadMap_.erase(it);
// 3. 更新总线程数:保证TotalThreadNum_统计准确
TotalThreadNum_.store(ThreadMap_.size());
}
else
{
++it; // 线程存活,继续遍历
}
}
// 所有线程已清理完毕:通知主线程(ThreadExitCv_)
if (ThreadMap_.empty())
{
ThreadExitCv_.notify_one();
}
}
}
/**
* @brief 获取任务队列大小(线程安全)
*/
size_t HighAvailabilityThreadPool::GetTaskQueueSize() const
{
std::lock_guard<std::mutex> lock(TaskMutex_); // 加锁保护tasks_访问
return tasks_.size();
}
/**
* @brief 获取活跃线程数(无锁,高效)
*/
size_t HighAvailabilityThreadPool::GetActiveThreadCount() const
{
return ActiveThreadNum_.load(); // 原子变量直接读取
}
/**
* @brief 获取总线程数(无锁,高效)
*/
size_t HighAvailabilityThreadPool::GetTotalThreadCount() const
{
return TotalThreadNum_.load(); // 原子变量直接读取
}
/**
* @brief 获取核心线程数(线程安全)
*/
size_t HighAvailabilityThreadPool::GetCoreThreadCount() const
{
std::lock_guard<std::mutex> lock(ThreadMutex_); // 加锁遍历ThreadMap_
size_t CoreCount = 0;
for (const auto &pair : ThreadMap_)
{
if (pair.second.IsCore.load()) // 统计核心线程(IsCore=true)
{
CoreCount++;
}
}
return CoreCount;
}
四、关键逻辑解析
1. 线程分层管理(核心 + 非核心)
核心线程
- 构造函数中创建,数量固定(
MinThreadNum_),常驻不退出; - 采用
wait()永久等待任务,只有线程池停止时才退出; - 保证基础并发能力,避免任务峰值时频繁创建线程。
非核心线程
- 任务队列满且活跃线程不足时动态创建(
submit()函数中); - 采用
wait_for()超时等待,空闲超时(IdleTimeoutMs_)后触发逻辑退出; - 退出时标记
IsAlive = false,由清理线程回收资源; - 应对任务峰值,峰值过后自动销毁,避免资源浪费。
2. 任务提交与封装
任务提交流程:
- 检查线程池状态和队列容量,避免无效提交;
- 用
std::bind绑定 "函数 + 参数",生成无参Task(统一任务格式); - 二次封装
Task,添加超时监控逻辑(ExecuteTaskWithTimeout); - 任务入队,唤醒工作线程执行。
关键避坑点 :lambda 中按值捕获 task,而非引用。因为 submit() 函数执行完毕后,局部变量 task 会销毁,引用捕获会导致悬垂引用,执行任务时崩溃。
3. 清理线程的核心作用
很多同学会疑惑:"非核心线程已经 break 退出了,为什么还需要 CleanUpDeadThreads?"答案是:break 只是 "逻辑退出",CleanUpDeadThreads 负责 "物理回收":
- 非核心线程
break后,worker()函数执行完毕,但std::thread实例仍存在于ThreadMap_中; std::thread必须调用join()才能回收系统线程资源(否则会成为僵尸线程,导致资源泄漏);- 清理线程每 1 秒遍历
ThreadMap_,找到IsAlive = false的线程,调用join()回收资源并从容器中删除。
4. 优雅停机流程
- 标记
IsStop_ = true,禁止新任务提交; - 调用
TaskCv_.notify_all(),唤醒所有等待任务的工作线程; - 停止清理线程,等待其
join()完成; - 等待
ThreadMap_为空(所有工作线程资源回收),确保无资源泄漏。
五、使用示例
cpp
#include <iostream>
#include <chrono>
#include "HighAvailabilityThreadPool.h"
// 测试任务:打印数字并休眠
void TestTask(int num)
{
std::cout << "Thread " << std::this_thread::get_id() << " execute task " << num << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 模拟任务执行耗时
}
// 超时任务:执行时间超过线程池配置的10秒
void TimeoutTask()
{
std::cout << "Execute timeout task" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(15)); // 15秒超时
}
int main()
{
try
{
// 创建线程池:核心线程2,最大线程4,非核心线程空闲超时3秒,任务超时10秒,队列最大50
HighAvailabilityThreadPool pool(2, 4, 3000, 10000, 50);
// 提交10个普通任务
for (int i = 0; i < 10; ++i)
{
pool.submit(TestTask, i);
}
// 提交1个超时任务
pool.submit(TimeoutTask);
// 打印线程池状态
std::cout << "Current task queue size: " << pool.GetTaskQueueSize() << std::endl;
std::cout << "Current total thread count: " << pool.GetTotalThreadCount() << std::endl;
std::cout << "Current core thread count: " << pool.GetCoreThreadCount() << std::endl;
// 等待任务执行(主线程休眠3秒)
std::this_thread::sleep_for(std::chrono::seconds(3));
// 再次打印状态(非核心线程可能因空闲超时退出)
std::cout << "After 3 seconds, total thread count: " << pool.GetTotalThreadCount() << std::endl;
std::cout << "Active thread count: " << pool.GetActiveThreadCount() << std::endl;
// 停止线程池(优雅停机)
pool.stop();
std::cout << "ThreadPool stopped" << std::endl;
}
catch (const std::exception &e)
{
std::cerr << "Exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
六、注意事项与优化方向
注意事项
- 日志库依赖:代码中使用
SPDLOG日志库,可替换为printf或其他日志实现; - 任务超时仅监控:当前仅打印超时告警,如需强制终止超时任务,需扩展线程取消逻辑(如
pthread_cancel,但需注意线程安全); - 线程数配置:最大线程数建议设为
CPU核心数×2,避免上下文切换过于频繁; - 任务队列限流:根据业务场景调整
MaxQueueSize,避免队列过大导致内存溢出。
优化方向
- 支持任务优先级:将
std::queue改为优先级队列(std::priority_queue),实现任务优先级调度; - 动态调整核心线程数:支持运行时修改核心线程数,适配不同业务负载;
- 任务结果返回:通过
std::future让主线程获取任务执行结果; - 线程池状态监控:添加回调函数,实时通知线程池状态变化(如队列满、线程扩容等);
- 强制终止超时任务:结合
std::thread+ 信号机制,实现超时任务的强制终止。
总结
本文实现的高可用线程池通过「核心线程常驻 + 非核心线程动态伸缩」解决了资源利用率与并发性能的平衡问题,同时提供了队列限流、任务超时监控、优雅停机等工业级特性,代码可直接复用在生产环境。
核心设计思路是 "分层管理 + 安全同步 + 资源回收":线程分层保证并发与节能,互斥锁 + 条件变量保证线程安全,清理线程保证资源不泄漏。希望通过本文的讲解,大家能不仅能 "用线程池",更能 "懂线程池",并根据实际业务场景进行扩展优化。