当前位置: 首页 > news >正文

【C++】线程池

C++ 线程池介绍

  1. 什么是线程池?
    线程池(Thread Pool) 是一种并发编程模型,用于管理和复用多个线程,避免频繁创建/销毁线程的开销。它通过预创建一组线程,并将任务提交到队列中,由空闲线程自动执行,从而提升多线程程序的性能和资源利用率。

  1. 为什么需要线程池?
  • 降低开销:线程创建/销毁成本高(涉及系统调用、内存分配)。
  • 任务调度优化:避免线程竞争CPU导致的上下文切换损耗。
  • 资源管控:限制并发线程数量,防止系统过载。
  • 简化编程:将任务提交与执行逻辑解耦。

适用场景
高并发服务器、批量数据处理、异步任务执行(如日志写入)、GUI后台计算等。

单队列线程池

简单高效,推荐使用!!!

实现

以下是基于C++11/17标准库的线程池简化实现

#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>class ThreadPool {
public:explicit ThreadPool(size_t num_threads = std::thread::hardware_concurrency()) {for (size_t i = 0; i < num_threads; ++i) {workers.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queue_mutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) return;task = std::move(tasks.front());tasks.pop();}task(); // 执行任务}});}}template<typename F, typename... Args>auto enqueue(F&& f, Args&&... args) -> std::future<decltype(f(args...))> {using return_type = decltype(f(args...));auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queue_mutex);if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");tasks.emplace([task] { (*task)(); });}condition.notify_one(); // 唤醒一个线程return res;}~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for (std::thread& worker : workers) {if (worker.joinable()) worker.join();}}private:std::vector<std::thread> workers;std::queue<std::function<void()>> tasks;std::mutex queue_mutex;std::condition_variable condition;bool stop = false;
};

线程池核心组件

组件作用
任务队列存储待执行的任务(std::queue<std::function<void()>>
工作线程组预创建的线程集合,循环从队列中取任务执行(std::vector<std::thread>
互斥锁保护任务队列的线程安全访问(std::mutex
条件变量协调线程的休眠与唤醒(std::condition_variable
停止标志安全终止所有线程(bool stop

使用示例

#include <iostream>int main() {ThreadPool pool(4); // 创建4个线程的线程池// 提交多个任务std::vector<std::future<int>> results;for (int i = 0; i < 8; ++i) {results.emplace_back(pool.enqueue([i] {std::this_thread::sleep_for(std::chrono::seconds(1));return i * i;}));}// 获取结果for (auto&& result : results) {std::cout << result.get() << ' ';}std::cout << std::endl;return 0;
}

输出0 1 4 9 16 25 36 49(执行时间约2秒,4线程并行)


优化方向

  1. 动态扩缩容:根据任务负载动态调整线程数量。
  2. 任务优先级:支持优先级队列(如std::priority_queue)。
  3. 任务窃取:允许空闲线程从其他线程的任务队列窃取任务。
  4. 返回值处理:使用std::futurestd::packaged_task传递异步结果。
  5. 异常处理:捕获任务中的异常并传递到主线程。

注意事项

  • 线程安全:确保任务队列的访问通过锁保护。
  • 资源释放:析构时需等待所有任务完成,避免资源泄漏。
  • 死锁风险:避免在任务中阻塞等待其他任务的结果。
  • 性能调优:根据硬件核心数设置合理线程数(通常为CPU核心数 + 1)。

通过线程池,可以显著提升C++程序的并发性能,同时保持代码的简洁与可维护性。

双队列线程池

总体来说,不推荐!!!


线程池是否可以使用两个队列(空闲队列 & 工作队列)?

在传统线程池设计中,通常使用单个任务队列来管理待执行的任务,所有工作线程共享此队列,并通过条件变量协调任务的获取与执行。但根据实际需求,可以通过两个队列(空闲线程队列 + 工作线程队列)来优化管理,以下是详细分析:


对比

设计方式单任务队列双队列(空闲+工作)
核心思想所有线程共享一个任务队列,竞争获取任务显式区分空闲线程和工作线程,按需调度
线程状态管理隐式(通过条件变量等待)显式(记录线程是否空闲)
适用场景通用场景,简单高效需要动态扩缩容、优先级调度等复杂场景
实现复杂度简单较高(需维护线程状态和队列同步)

双队列设计的意义

  • 动态线程管理
    根据任务负载动态调整活跃线程数量,例如:
    • 当任务队列积压时,从空闲队列唤醒更多线程。
    • 当任务减少时,将空闲线程挂起或释放,减少资源占用。
  • 优先级调度
    允许为不同优先级的任务分配独立队列,结合线程状态实现更精细的调度。
  • 避免过度竞争
    将空闲线程与工作线程分离,减少锁争用(例如:空闲队列单独管理)。

双队列实现示例

以下是一个基于空闲队列和工作队列的线程池框架:

#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>class ThreadPool {
public:explicit ThreadPool(size_t max_threads) : max_threads(max_threads) {}void enqueue(std::function<void()> task) {{std::unique_lock<std::mutex> lock(task_mutex);tasks.push(std::move(task));}// 尝试唤醒空闲线程或创建新线程if (!idle_threads.empty()) {std::unique_lock<std::mutex> lock(idle_mutex);auto thread = idle_threads.front();idle_threads.pop();lock.unlock();thread->assign_task(); // 唤醒空闲线程} else if (active_threads < max_threads) {create_thread(); // 创建新线程}}private:size_t max_threads;size_t active_threads = 0;std::queue<std::function<void()>> tasks;std::mutex task_mutex;// 空闲线程队列(存储可复用的线程对象)std::queue<std::shared_ptr<WorkerThread>> idle_threads;std::mutex idle_mutex;class WorkerThread {public:void assign_task(std::function<void()> task) {{std::unique_lock<std::mutex> lock(mtx);current_task = std::move(task);has_task = true;}cv.notify_one();}void run() {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [&] { return has_task || terminate; });if (terminate) return;task = std::move(current_task);has_task = false;}task(); // 执行任务// 任务完成后,将自己放回空闲队列pool->return_to_idle(this);}}bool is_idle() const { return !has_task; }private:std::mutex mtx;std::condition_variable cv;std::function<void()> current_task;bool has_task = false;bool terminate = false;};void create_thread() {auto worker = std::make_shared<WorkerThread>();std::thread([worker] { worker->run(); }).detach();active_threads++;}void return_to_idle(WorkerThread* thread) {std::unique_lock<std::mutex> lock(idle_mutex);idle_threads.push(thread);}
};

双队列的优缺点

优点缺点
动态扩缩容:按需增加/减少活跃线程实现复杂度高(需管理线程生命周期)
减少锁竞争(空闲与工作线程分离)上下文切换可能增多(线程频繁唤醒/挂起)
支持更复杂的调度策略(如优先级)内存占用更高(需维护额外队列)

适用场景

  • 突发性高负载:任务量波动大时,动态调整线程数量。
  • 资源敏感环境:需严格控制线程数量(如嵌入式系统)。
  • 任务优先级调度:高优先级任务优先分配空闲线程。

总结

  • 单队列足够大多数场景:简单高效,推荐优先使用。
  • 双队列适合特殊需求:当需要动态扩缩容、优先级调度或减少锁竞争时,可考虑双队列设计,但需权衡实现复杂度与性能收益。

根据具体需求选择设计,避免过度工程化!

http://www.xdnf.cn/news/195841.html

相关文章:

  • 【问题】docker容器修改环境变量的方式
  • SplitReason:在复杂步骤借助更大尺寸模型推理,1.5B+32B,实现准确率28%提升+8倍速度提升
  • 编程日志4.23
  • 【Linux内核设计与实现】第三章——进程管理05
  • SSO单点登录
  • 通过DeepSeek大语言模型控制panda机械臂,听懂人话,拟人性回答。智能机械臂助手又进一步啦
  • 大模型在肝硬化腹水风险预测及临床方案制定中的应用研究
  • AWS虚拟专用网络全解析:从基础到高级实践
  • 【Spark入门】Spark架构解析:组件与运行机制深度剖析
  • vim粘贴代码格式错乱 排版错乱 缩进错乱 解决方案
  • 【软件工程】需求分析详解
  • 24体育NBA足球直播M28模板体育赛事直播源码
  • 介绍下Nginx的作用与请求转发机制
  • Windows操作系统核心知识解析
  • C++ 表达式求值优先级、结合律与求值顺序(五十九)
  • 关于https请求丢字符串导致收到报文解密失败问题
  • 第二章:Agent System
  • RestRequest ,newtonsoft解析
  • 大模型(LLMs)强化学习—— PPO
  • 【angular19】入门基础教程(一):项目的搭建与启动
  • 如何查看电脑电池使用情况
  • 北京市延庆区“禅苑茶事“非遗项目挂牌及茶事院正式启用
  • Adobe Lightroom Classic v14.3.0.8 一款专业的数字摄影后期处理软件
  • 测试反馈陷入死循环?5大策略拆解新旧Bug难题
  • if consteval
  • 多模态大型模型,实现以人为中心的精细视频理解
  • [原创](现代Delphi 12指南):[macOS 64bit App开发]: 跨平台开发同样支持retain()引用计数器处理.
  • 【氮化镓】质子辐照对 GaN-on-GaN PiN 二极管电导调制的影响
  • 后端Web实战之登录认证,JWT令牌,过滤器Filter,拦截器Interceptor一篇文章so easy!!!
  • 【python】-基础语法1