C++并发编程之五 高级线程管理

news/2024/10/22 15:29:23/

文章目录

    • 5.1.1 线程池

5.1.1 线程池

在前面我们引入了线程的通信和同步手段,那么为什么还要引入线程池呢?
线程池是一种管理多个线程的技术,它可以减少线程的创建和销毁的开销,提高并发性能。线程池中有一定数量的空闲线程,当有新的任务到来时,就从池中分配一个线程来执行任务,当任务完成后,线程返回池中等待下一个任务。这样可以避免频繁地创建和销毁线程,节省资源和时间。

用C++11实现线程池的基本思路是:

  1. 创建一个任务队列,用来存放用户提交的任务。
  2. 创建一个线程队列,用来存放一定数量的空闲线程。
  3. 使用互斥锁和条件变量来同步任务队列和线程队列的访问。
  4. 当有新的任务到来时,将任务放入任务队列,并通知一个空闲线程去执行它。
  5. 当一个线程完成了任务后,将自己放回线程队列,并等待下一个任务。
  6. 当用户想要停止线程池时,向所有线程发送停止信号,并等待它们结束。

下面是一个简单的示例代码:

#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>class ThreadPool {
public:// 构造函数,创建指定数量的线程ThreadPool(size_t num_threads) : stop(false) {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(this->queue_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();}});}}// 向任务队列中添加一个新的任务template<class F, class... Args>void enqueue(F&& f, Args&&... args) {std::function<void()> task = std::bind(std::forward<F>(f), std::forward<Args>(args)...); {std::unique_lock<std::mutex> lock(queue_mutex);tasks.emplace(task);}condition.notify_one();}// 停止所有线程并回收资源~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for (std::thread& worker : workers) worker.join();}private:std::vector<std::thread> workers; // 线程队列std::queue<std::function<void()>> tasks; // 任务队列std::mutex queue_mutex; // 互斥锁std::condition_variable condition; // 条件变量bool stop; // 停止标志
};// 测试函数,打印一些信息
void print(int i) {std::cout << "Hello from thread " << i << "\n";
}int main() {ThreadPool pool(4); // 创建4个线程的线程池for (int i = 0; i < 10; ++i) pool.enqueue(print, i); // 添加10个测试任务}
  1. bar(std::forward<Args>(args)…) 中的 … 是函数参数包展开语法,它会将 args中的每个参数展开成一个单独的函数参数。这意味着,如果 args是一个包含多个参数的参数包,那么这个展开语法将会在函数调用时展开成多个函数参数。
  2. 例如,假设 args 包含两个参数,一个 int 和一个 double,那么bar(std::forward(args)…) 将会被展开成 bar(std::forward(arg1), std::forward(arg2)),其中 arg1 和 arg2 分别代表参数包中的第一个参数和第二个参数。
  3. 因此,使用函数参数包展开语法可以将参数包中的参数展开成多个单独的函数参数,从而使得参数可以被正确地转发给其他函数或进行其他操作。
  4. 在展开语法中,…符号前面的内容是函数模板中的参数包名称(例如:T),而…符号本身是展开语法符号,用于告诉编译器要将参数包展开成一系列参数,并将它们传递给函数func。
  5. 在这个例子中,逗号操作符不是必需的,因为参数包展开符号已经足够明确地告诉编译器要将参数包展开成一系列参数。

http://www.ppmy.cn/news/32418.html

相关文章

关于我拒绝了腾讯测试开发岗offer这件事

2022年刚开始有了向要跳槽的想法&#xff0c;之前的公司不能算大厂但在重庆也算是数一数二。开始跳槽的的时候我其实挺犹豫的 其实说是有跳槽的想法在2022年过年的时候就有了&#xff0c;因为每年公司3月会有涨薪的机会&#xff0c;所以想着看看那能不能涨&#xff08;其实还是…

P1078 [NOIP2012 普及组] 文化之旅 Floyd

题目描述有一位使者要游历各国&#xff0c;他每到一个国家&#xff0c;都能学到一种文化&#xff0c;但他不愿意学习任何一种文化超过一次&#xff08;即如果他学习了某种文化&#xff0c;则他就不能到达其他有这种文化的国家&#xff09;。不同的国家可能有相同的文化。不同文…

Java实现调用第三方相关接口(附详细思路)

目录1.0.简单版2.0.升级版2-1.call.timeout()怎么传入新的超时值2-2.timeout(10, TimeUnit.SECONDS)两个参数的意思&#xff0c;具体含义3.0.进阶版3-1.java.net.SocketTimeoutException: 超时如何解决4.0.终极版1.0.简单版 以下是一个使用 Java 实际请求“第三方”的简单示例代…

有什么比较好的bug管理工具?5款热门工具推荐

工具再优秀&#xff0c;适合自己才最重要。 为尽量讲透这个问题&#xff0c;本文的行文结构我先整理如下&#xff1a; 1、为什么需要bug管理工具&#xff1f; 2、好的bug管理工具的标准是什么&#xff1f; 3、好的bug管理工具推荐&#xff08;5款&#xff09; 4、如何挑选适合…

【分享】用友U8无需代码连接钉钉宜搭的方法

用友U8用户使用场景&#xff1a; 企业使用用友U8快速整合记账管理、报账统计工作&#xff0c;每当用友U8有新的单据时&#xff0c;还需财务人员查询单据详情&#xff0c;并同步到钉钉宜搭表单中进行汇总&#xff0c;便于相关负责人查看。但人工手动同步数据不仅费时费力且易出错…

java-正装照换底色小demo-技术分享

文章目录前言java-正装照换底色小demo-技术分享01 实现思路02 效果02::01 原图:02::02 执行单元测试:02::03 效果:03 编码实现前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞…

单片机——IIC协议与24C02

1、基础知识 1.1、IIC串行总线的组成及工作原理 I2C总线只有两根双向信号线。一根是数据线SDA&#xff0c;另一根是时钟线SCL。 1.2、I2C总线的数据传输 I2C总线进行数据传送时&#xff0c;时钟信号为高电平期间&#xff0c;数据线上的数据必须保持稳定&#xff0c;只有在时钟…

Linux操作系统基础的常用命令

1&#xff0c;Linux简介Linux是一种自由和开放源码的操作系统&#xff0c;存在着许多不同的Linux版本&#xff0c;但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中&#xff0c;比如手机、平板电脑、路由器、台式计算机。1.1Linux介绍Linux出现于1991年&#xff0c…