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

C++线程池的使用

线程池的基本概念

线程池(Thread Pool)是一种多线程处理形式,它预先创建一组线程并保存在"池"中,当有任务到来时,从池中取出一个线程来执行任务,任务执行完毕后线程返回池中等待下一个任务,而不是销毁线程。

线程池的核心作用

  1. 降低资源消耗:通过重复利用已创建的线程,减少线程创建和销毁的开销

  2. 提高响应速度:任务到达时可以直接使用已有线程,无需等待线程创建

  3. 提高线程的可管理性:可以统一分配、调优和监控线程资源

  4. 防止系统过载:通过限制最大线程数,避免因创建过多线程导致系统崩溃

线程池的基本组成

  1. 任务队列:存储待执行的任务

  2. 工作线程集合:执行任务的线程集合

  3. 线程管理器:负责创建、销毁线程,管理线程生命周期

  4. 任务接口:任务提交的接口

C++ 线程池的实现原理

1. 任务队列实现

#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>class ThreadPool {
private:std::queue<std::function<void()>> tasks;std::mutex tasks_mutex;std::condition_variable condition;// ...
};

2. 工作线程实现

class ThreadPool {
private:std::vector<std::thread> workers;bool stop = false;void worker_thread() {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(this->tasks_mutex);this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });if (this->stop && this->tasks.empty())return;task = std::move(this->tasks.front());this->tasks.pop();}task();}}
public:ThreadPool(size_t threads) {for (size_t i = 0; i < threads; ++i)workers.emplace_back([this] { worker_thread(); });}// ...
};

3. 任务提交接口 

template<class F, class... Args>
auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;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(tasks_mutex);if(stop)throw std::runtime_error("enqueue on stopped ThreadPool");tasks.emplace([task](){ (*task)(); });}condition.notify_one();return res;
}

线程池的工作流程

  1. 初始化阶段

    • 创建固定数量的工作线程

    • 所有线程进入等待状态

  2. 任务提交阶段

    • 用户通过enqueue方法提交任务

    • 任务被封装并放入任务队列

    • 通知一个等待中的线程

  3. 任务执行阶段

    • 工作线程被唤醒

    • 从队列中取出任务并执行

    • 执行完成后继续等待新任务

  4. 终止阶段

    • 设置停止标志

    • 通知所有线程

    • 等待所有线程结束

高级特性实现

 1. 动态线程调整

void adjust_threads(size_t new_size) {if (new_size < workers.size()) {// 减少线程数{std::unique_lock<std::mutex> lock(tasks_mutex);stop = true;}condition.notify_all();for (std::thread &worker : workers)worker.join();workers.clear();stop = false;workers.reserve(new_size);for (size_t i = 0; i < new_size; ++i)workers.emplace_back([this] { worker_thread(); });} else if (new_size > workers.size()) {// 增加线程数for (size_t i = workers.size(); i < new_size; ++i)workers.emplace_back([this] { worker_thread(); });}
}

 2. 任务优先级

struct Task {std::function<void()> func;int priority;bool operator<(const Task& other) const {return priority < other.priority; // 优先级数字越小优先级越高}
};std::priority_queue<Task> tasks;template<class F, class... Args>
auto enqueue(int priority, F&& f, Args&&... args) {// ... 类似前面的实现,但使用优先级队列
}

 实际应用示例

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

性能优化技巧

  1. 任务窃取(Work Stealing):当某个线程的任务队列为空时,可以从其他线程的任务队列尾部"窃取"任务执行

  2. 避免虚假唤醒:使用条件变量时总是使用谓词检查,防止意外唤醒

  3. 批量任务提交:一次性提交多个任务可以减少锁竞争

  4. 线程本地任务队列:每个线程维护自己的任务队列,减少竞争

常见线程池实现对比

  1. 简单线程池:固定线程数,简单任务队列

  2. 动态线程池:可根据负载动态调整线程数量

  3. 优先级线程池:支持不同优先级的任务

  4. 任务窃取线程池:提高负载均衡能力

注意事项

  1. 异常处理:确保任务中的异常不会导致线程退出

  2. 死锁预防:避免任务之间相互等待

  3. 资源限制:合理设置最大线程数

  4. 线程安全:确保所有共享数据的访问都是线程安全的

线程池是多线程编程中的重要工具,合理使用可以显著提高程序性能,但需要仔细设计和实现以避免各种并发问题。

 

线程池的核心作用

作用详细说明类比
1. 减少线程生命周期开销避免频繁创建/销毁线程(系统调用成本高)
- 线程创建:需分配栈空间(默认MB级)、初始化内核资源
- 线程销毁:需回收资源、处理线程局部存储
像"复用工人"而非临时招聘
2. 控制并发度限制最大线程数,防止:
- 系统资源耗尽(内存、CPU过载)
- 过多线程导致频繁上下文切换(性能下降)
像"限流阀"保护系统
3. 任务调度优化- 统一管理任务队列
- 支持优先级/定时任务
- 实现负载均衡(如work-stealing)
像"智能任务分配中心"
4. 响应速度提升任务到达时直接由空闲线程执行
(无需等待线程创建)
像"随时待命的服务团队"
5. 资源隔离可为不同业务分配独立线程池
(避免互相影响)
像"专用通道"
http://www.xdnf.cn/news/8519.html

相关文章:

  • 力扣 128.最长连续序列
  • 缓存和数据库一致性问题
  • 对于geoserver发布数据后的开发应用
  • MYSQL之复合查询
  • 基于51单片机和8X8点阵屏、独立按键的飞行躲闪类小游戏
  • wordpress上传图片时出现服务器无法处理图片
  • Vue3 + Element Plus表格筛选样式设置
  • ES6 哈希数据结构
  • 【maxcompute】阿里maxcompute Python开发个人经验汇总
  • 为何在VMware中清理CentOS虚拟机后,本地磁盘空间未减少的问题解决
  • 工业RTOS生态重构:从PLC到“端 - 边 - 云”协同调度
  • PCIe Gen3 phy(编解码,token相关)
  • 谢飞机的Java面试奇遇:AIO、BIO、NIO与Netty深度解析
  • openEuler 22.03安装zabbix7 LTS(容器化部署)
  • ajax中get和post的区别,datatype返回的数据类型有哪些?
  • STM32 SPI通信(硬件)
  • 2025 最新教程:注册并切换到美区 Apple ID
  • 3dczml时间动态图型场景
  • 怎么判断一个Android APP使用了taro 这个跨端框架
  • 自制操作系统day9内存管理(cache、位图、列表管理、内存的释放)(ai辅助整理)
  • Web前端开发:JavaScript的使用
  • 【软件安装】Windows操作系统中安装mongodb数据库和mongo-shell工具
  • 从零入门:Google Cloud Platform (GCP) 基础架构与核心服务全解析
  • 推荐一款滴滴团队开源流程图编辑框架logic-flow
  • 禅道——安装PHP的ioncube扩展
  • 101 alpha_59
  • 精度不够?光纤激光尺0.2ppm误差解锁微米级制造
  • 粗糙表面生成程序及模拟方法
  • PCB设计实践(二十六)贴片电容与插件电容的全面解析:差异、演进与应用场景
  • 【MySQL】07.内置函数