C++ 高并发线程池实战(二):动态缓存线程池 + 调用者运行拒绝策略完整版实现

在上一篇文章中,我们实现了固定线程池 FixedThreadPool ,并完成了同步队列优化 + 调用者运行拒绝策略 ,解决了任务队列满时任务丢失的问题。但固定线程池在面对大量短期异步任务、IO 密集型任务时,线程数固定无法弹性伸缩,容易导致任务等待、吞吐量不足。

因此,本文在前文基础上,全新实现 CachedThreadPool 动态缓存线程池 ,并完整复用之前的同步队列、超时等待、调用者运行拒绝策略 ,打造一套可自动扩缩容、线程超时回收、高容错、高可用的生产级线程池。

一、CachedThreadPool 核心设计思想

缓存线程池 = 动态弹性线程池核心特性:

  1. 线程数不固定:来任务自动创建新线程,直到最大线程数
  2. 空闲线程自动回收:线程空闲超过指定时间自动销毁,节约资源
  3. 复用同步队列:直接使用上一篇优化好的 SyncQueue,支持超时等待
  4. 自带拒绝策略:队列满 → 触发调用者运行,保证任务不丢失、不阻塞
  5. 优雅启停:线程安全退出,不崩溃、不泄漏

适用场景:

  • 高频短生命周期任务(网络请求、日志输出、消息处理)
  • IO 密集型业务(文件读写、网络收发)
  • 需要高并发、低延迟的后端服务

二、核心组件总览

本次实现包含 3 个核心文件:

  1. SyncQueue_1.hpp:复用前作,任务同步队列(超时等待 + 返回值状态)
  2. CachedThreadPool.hpp:动态缓存线程池头文件
  3. CachedThreadPool.cpp:动态线程池实现(自动扩容 + 空闲回收)
  4. Test04_20_Cache.cpp:测试用例(高并发任务提交)

所有代码完全继承前作优点 ,不再重复解释基础部分,只讲解动态线程池新增逻辑

三、CachedThreadPool.hpp 头文件逐行解析

cpp 复制代码
#include "SyncQueue_1.hpp"
#include <functional>
#include <thread>
#include <mutex>
#include <list>
#include <memory>
#include <atomic>
#include <algorithm>

#ifndef CACHED_THREAD_POOL_HPP
#define CACHED_THREAD_POOL_HPP

namespace tulun
{
    class CachedThreadPool
    {
    public:
        using TaskType = std::function<void(void)>;

    private:
        // 线程组:保存当前所有运行的线程
        std::list<std::shared_ptr<std::thread>> m_threadGroup;
        // 同步队列:直接复用前作(超时等待 + 状态返回)
        tulun::SyncQueue<TaskType> m_queue;

        // 空闲线程数量(原子变量,线程安全)
        std::atomic<int> m_idleThreadNum;
        // 线程池运行状态
        std::atomic<bool> m_isRunning;
        // 保证 Stop 只执行一次
        std::once_flag m_onceFlag;
        // 互斥锁:保护线程组操作
        std::mutex m_mutex;

        // 最大线程数(防止无限创建线程)
        const int m_maxThreadNum;
        // 线程空闲时间(超时自动销毁)
        const int m_idleTimeMs;

    private:
        // 线程主函数
        void runInThread();
        // 创建一个新线程
        void addOneThread();
        // 停止线程组
        void stopThreadGroup();

    public:
        explicit CachedThreadPool(int maxThread = 50, int idleTimeMs = 50);
        ~CachedThreadPool();

        // 禁止拷贝
        CachedThreadPool(const CachedThreadPool&) = delete;
        CachedThreadPool& operator=(const CachedThreadPool&) = delete;

        // 提交任务(支持左值、右值)
        void addTask(TaskType&& task);
        void addTask(const TaskType& task);

        // 停止线程池
        void stop();
    };
}

#endif

关键成员作用

  1. m_threadGroup用链表保存所有线程,方便线程退出时自动移除。

  2. m_queue 直接复用前作同步队列,自带超时等待 + 队列满返回错误

  3. m_idleThreadNum 记录当前空闲线程数,决定是否需要创建新线程

  4. m_maxThreadNum最大线程数,防止无限创建导致系统崩溃。

  5. m_idleTimeMs线程空闲时间,超时自动退出,实现自动回收。


四、CachedThreadPool.cpp 实现逐段详解

4.1 构造函数

cpp 复制代码
CachedThreadPool::CachedThreadPool(int maxThread, int idleTimeMs)
    : m_maxThreadNum(maxThread),
      m_idleTimeMs(idleTimeMs),
      m_queue(100), // 队列大小100
      m_isRunning(true),
      m_idleThreadNum(0)
{
}
  • 启动时不预创建线程,懒加载模式
  • 来任务才创建线程,节约系统资源

4.2 线程主函数 runInThread(最核心)

cpp 复制代码
void CachedThreadPool::runInThread()
{
    m_idleThreadNum++;

    while (m_isRunning)
    {
        TaskType task;
        // 阻塞获取任务,带超时时间
        bool ok = m_queue.take(task, m_idleTimeMs);

        if (!ok)
        {
            // 超时 → 退出循环 → 线程销毁
            break;
        }

        // 取到任务 → 不再空闲
        m_idleThreadNum--;
        if (task)
        {
            task();
        }
        // 执行完 → 变回空闲
        m_idleThreadNum++;
    }

    // 线程退出:空闲数-1
    m_idleThreadNum--;

    // 从线程组移除自己(线程安全)
    std::lock_guard<std::mutex> lock(m_mutex);
    for (auto it = m_threadGroup.begin(); it != m_threadGroup.end(); ++it)
    {
        if ((*it)->get_id() == std::this_thread::get_id())
        {
            m_threadGroup.erase(it);
            break;
        }
    }
}

灵魂逻辑解释

  1. 线程启动 → 空闲数 + 1
  2. take (task, 超时时间)
    • 有任务 → 执行
    • 没任务 → 超时退出
  3. 执行任务时:空闲 - 1
  4. 执行完成:空闲 + 1
  5. 超时退出:线程销毁,自动从线程组删除

这就是动态线程自动创建 + 自动回收的完整实现。


4.3 添加任务(带拒绝策略)

cpp 复制代码
void CachedThreadPool::addTask(TaskType&& task)
{
    // 尝试放入队列
    int ret = m_queue.put(std::forward<TaskType>(task));

    // 队列满/停止 → 调用者运行
    if (ret != 0)
    {
        task();
        return;
    }

    // 没有空闲线程 && 没达到最大线程数 → 创建新线程
    if (m_idleThreadNum == 0 && m_threadGroup.size() < m_maxThreadNum)
    {
        addOneThread();
    }
}

void CachedThreadPool::addTask(const TaskType& task)
{
    int ret = m_queue.put(task);
    if (ret != 0)
    {
        task();
        return;
    }

    if (m_idleThreadNum == 0 && m_threadGroup.size() < m_maxThreadNum)
    {
        addOneThread();
    }
}

关键逻辑(高并发精髓)

  1. 任务入队
  2. 失败 → 立即调用 task ()(调用者运行拒绝策略)
  3. 成功 → 判断是否有空闲线程
  4. 无空闲线程 → 立即创建新线程

来一个任务开一个线程,直到最大线程数


4.4 创建线程

cpp 复制代码
void CachedThreadPool::addOneThread()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    auto th = std::make_shared<std::thread>(&CachedThreadPool::runInThread, this);
    m_threadGroup.push_back(th);
}

4.5 优雅停止

cpp 复制代码
void CachedThreadPool::stop()
{
    std::call_once(m_onceFlag, &CachedThreadPool::stopThreadGroup, this);
}

void CachedThreadPool::stopThreadGroup()
{
    m_isRunning = false;
    m_queue.stop();

    for (auto& ptr : m_threadGroup)
    {
        if (ptr->joinable())
        {
            ptr->join();
        }
    }
}

五、同步队列 SyncQueue_1 关键部分(复用前作)

cpp 复制代码
// 带超时的 take
bool take(T& task, int waitMs = -1);

// put 返回状态:
// 0=成功 1=队列满 2=已停止
int put(T&& task);
int put(const T& task);

这让CachedThreadPool可以直接:

  • 队列满 → 返回 1 → 触发拒绝策略
  • 超时获取任务 → 实现线程回收

六、测试用例 Test04_20_Cache.cpp

cpp 复制代码
#include "CachedThreadPool.hpp"
#include <iostream>
using namespace std;

void func(int x)
{
    cout << "task run: " << x << endl;
}

int main()
{
    // 最大20个线程,空闲50ms自动销毁
    tulun::CachedThreadPool pool(20, 50);

    // 一次性提交1000个任务
    for (int i = 0; i < 1000; i++)
    {
        pool.addTask(std::bind(func, i));
    }

    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 0;
}

运行效果

  1. 任务进来 → 自动创建线程
  2. 线程数迅速升到 20
  3. 任务执行完 → 50ms 后线程自动退出
  4. 队列满 → 主线程执行任务(拒绝策略)
  5. 无内存泄漏、无阻塞、无丢失

七、本篇核心亮点总结

  1. 动态弹性伸缩无任务→线程自动销毁;高并发→自动创建线程,直到上限。

  2. 完美复用前作同步队列、超时机制、返回值状态全部复用,代码不冗余。

  3. 工业级拒绝策略:调用者运行队列满 → 提交线程自己执行,保证任务不丢失。

  4. 线程安全原子变量 + 互斥锁 + 条件变量,多线程高并发安全。

  5. 优雅停止线程自行退出、自行从线程组移除,不崩溃、不残留。

  6. 极高性能特别适合短任务、IO 密集型、高并发后端服务。


八、全文总结(可直接用于文章结尾)

本文基于上一篇固定线程池与同步队列的成果,实现了高性能动态缓存线程池 CachedThreadPool ,具备自动扩容、自动缩容、空闲超时回收、调用者运行拒绝策略等企业级特性。整套代码轻量、无依赖、C++11 标准实现,既解决了固定线程池吞吐不足的问题,又避免了无限制创建线程的风险,是高并发后端开发中必备的基础组件。

相比于固定线程池,缓存线程池更适合网络服务、日志系统、消息队列、IO 密集型业务,也是面试中高频考察的线程池实现方案。

相关推荐
气宇轩昂固执狂2 小时前
01-初识C语言
c语言·开发语言
t***5442 小时前
如何在 Dev-C++ 中使用 Clang 编译器
开发语言·c++
cany10002 小时前
C++ - 智能指针
开发语言·c++
我要升天!2 小时前
C语言连接 MySQL:libmysqlclient 获取方式详解
c语言·开发语言·数据库·mysql·adb
angushine2 小时前
Python常用方法
开发语言·前端·python
潜创微科技3 小时前
CH9245:双 Type‑C 转 PD 芯片方案,便携显示与拓展坞的理想选择
c语言·开发语言
Emberone3 小时前
深入理解 C++ STL string:从接口使用到底层模拟实现
c++·stl
【 】4233 小时前
pyhon相对导入
开发语言·python
小同志003 小时前
IoC 详解
java·开发语言