C++ 高可用线程池实现:核心 / 非核心线程动态扩缩容 + 任务超时监控

前言

在多线程编程中,线程池是解决 "线程创建销毁开销大""资源占用失控" 的核心方案。普通线程池往往存在 "线程数固定导致并发不足" 或 "空闲线程过多浪费资源" 的问题,本文实现的高可用线程池通过「核心线程常驻 + 非核心线程动态伸缩」+「任务超时监控」+「队列限流」等特性,兼顾了并发性能与资源利用率,适用于高并发、高可用的业务场景。

本文将从设计思路→核心特性→完整代码→关键逻辑解析→使用示例→注意事项逐步展开,代码可直接复用,同时附带详细注释和原理讲解,帮助大家理解线程池的核心设计。

一、设计思路

线程池的核心目标是 "高效管理线程资源,合理调度任务执行",本次设计围绕以下 3 个核心思路展开:

  1. 线程分层管理:核心线程(常驻不退出)保证基础并发能力,非核心线程(空闲超时销毁)应对突发任务峰值,实现资源动态伸缩;
  2. 任务安全调度:通过互斥锁 + 条件变量保证任务队列的线程安全访问,避免竞态条件;
  3. 高可用防护:任务队列限流防止内存溢出,任务超时监控便于问题排查,优雅停机保证资源不泄漏。

二、核心特性

  1. 动态扩缩容 :核心线程常驻,非核心线程空闲超时(默认 3 秒)自动销毁,任务峰值时最多扩容至CPU核心数×2(可配置);
  2. 队列限流:任务队列最大容量可配置(默认 500),队列满时拒绝新任务,避免内存无限增长;
  3. 任务超时监控:记录每个任务执行耗时,超时(默认 10 秒)打印告警日志(支持扩展强制终止逻辑);
  4. 线程安全:原子变量 + 互斥锁 + 条件变量三重保障,避免竞态条件和数据竞争;
  5. 优雅停机:停止时等待所有任务执行完毕,回收所有线程资源,无僵尸线程和资源泄漏;
  6. 状态查询:支持查询任务队列大小、活跃线程数、总线程数、核心线程数等状态。

三、完整实现代码

头文件(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. 任务提交与封装

任务提交流程:

  1. 检查线程池状态和队列容量,避免无效提交;
  2. std::bind 绑定 "函数 + 参数",生成无参 Task(统一任务格式);
  3. 二次封装 Task,添加超时监控逻辑(ExecuteTaskWithTimeout);
  4. 任务入队,唤醒工作线程执行。

关键避坑点 :lambda 中按值捕获 task,而非引用。因为 submit() 函数执行完毕后,局部变量 task 会销毁,引用捕获会导致悬垂引用,执行任务时崩溃。

3. 清理线程的核心作用

很多同学会疑惑:"非核心线程已经 break 退出了,为什么还需要 CleanUpDeadThreads?"答案是:break 只是 "逻辑退出",CleanUpDeadThreads 负责 "物理回收"

  • 非核心线程 break 后,worker() 函数执行完毕,但 std::thread 实例仍存在于 ThreadMap_ 中;
  • std::thread 必须调用 join() 才能回收系统线程资源(否则会成为僵尸线程,导致资源泄漏);
  • 清理线程每 1 秒遍历 ThreadMap_,找到 IsAlive = false 的线程,调用 join() 回收资源并从容器中删除。

4. 优雅停机流程

  1. 标记 IsStop_ = true,禁止新任务提交;
  2. 调用 TaskCv_.notify_all(),唤醒所有等待任务的工作线程;
  3. 停止清理线程,等待其 join() 完成;
  4. 等待 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;
}

六、注意事项与优化方向

注意事项

  1. 日志库依赖:代码中使用 SPDLOG 日志库,可替换为 printf 或其他日志实现;
  2. 任务超时仅监控:当前仅打印超时告警,如需强制终止超时任务,需扩展线程取消逻辑(如 pthread_cancel,但需注意线程安全);
  3. 线程数配置:最大线程数建议设为 CPU核心数×2,避免上下文切换过于频繁;
  4. 任务队列限流:根据业务场景调整 MaxQueueSize,避免队列过大导致内存溢出。

优化方向

  1. 支持任务优先级:将 std::queue 改为优先级队列(std::priority_queue),实现任务优先级调度;
  2. 动态调整核心线程数:支持运行时修改核心线程数,适配不同业务负载;
  3. 任务结果返回:通过 std::future 让主线程获取任务执行结果;
  4. 线程池状态监控:添加回调函数,实时通知线程池状态变化(如队列满、线程扩容等);
  5. 强制终止超时任务:结合 std::thread + 信号机制,实现超时任务的强制终止。

总结

本文实现的高可用线程池通过「核心线程常驻 + 非核心线程动态伸缩」解决了资源利用率与并发性能的平衡问题,同时提供了队列限流、任务超时监控、优雅停机等工业级特性,代码可直接复用在生产环境。

核心设计思路是 "分层管理 + 安全同步 + 资源回收":线程分层保证并发与节能,互斥锁 + 条件变量保证线程安全,清理线程保证资源不泄漏。希望通过本文的讲解,大家能不仅能 "用线程池",更能 "懂线程池",并根据实际业务场景进行扩展优化。

相关推荐
多多想1 小时前
C++扫盲——为什么C/C++分文件要写h和cpp?
c语言·c++
liulilittle1 小时前
C++判断wchar_t空白字符
开发语言·c++
晨非辰2 小时前
算法闯关日记 Episode :解锁链表「环形」迷局与「相交」奥秘
数据结构·c++·人工智能·后端·python·深度学习·神经网络
Rock_yzh3 小时前
LeetCode算法刷题——560. 和为 K 的子数组
数据结构·c++·学习·算法·leetcode·职场和发展·哈希算法
liulilittle4 小时前
C++ 17 字符串填充函数(PaddingLeft、PaddingRight)填充左侧、右侧。
c++·算法
AuroraWanderll4 小时前
深入理解C++多态(三):多态的原理——虚函数表机制(上)
c语言·开发语言·数据结构·c++·算法·stl
阿沁QWQ4 小时前
STL库vector模拟实现
开发语言·c++
Fcy6485 小时前
C++ 模版(进阶)(含array解析)
开发语言·c++·stl·array·模版
Minecraft红客5 小时前
复原大唐3d更新版本
c++·游戏·3d·娱乐