Thread Pool 线程池

2023-11-06

Thread Pool

本文基于经典的99行代码,稍加修改使其支持C++20,并增加了wait与join功能。

#pragma once
#include <thread>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <future>
#include <functional>
#include <stdexcept>

namespace ThreadPool {
	class ThreadPool {
	public:
		ThreadPool(size_t);
		template<class F, class... Args>
		auto enqueue(F&& f, Args&&... args)
			->std::future<typename std::invoke_result<F, Args...>::type>;
		~ThreadPool();
		void wait();
		void join();
	private:
		std::vector<std::thread> workers; //线程池
		std::queue<std::function<void()>> tasks; //任务队列
		std::mutex queue_mutex; //队列锁
		std::condition_variable condition; //状态变量
		std::atomic<bool> stop; //原子变量
		std::vector<unsigned char> status;
	};

	// the constructor just launches some amount of workers
	inline ThreadPool::ThreadPool(size_t threads) : stop(false), status(threads, 0)
	{
		for (size_t i = 0; i < threads; ++i)
		{
			workers.emplace_back( //增加工作线程
				[this, i]
				{
					for (;;) //线程死循环
					{
						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();
							//std::cout << "Thread " << i << " is working" << std::endl;
							//std::cout << "The task size is " << this->tasks.size() << std::endl;
						}
						this->status[i] = true;
						task(); //执行任务
						this->status[i] = false;
					}
				}
				);
		}
	}

	// add new work item to the pool
	template <class F, class... Args>
	auto ThreadPool::enqueue(F&& f, Args&&... args)
		-> std::future<typename std::invoke_result<F, Args...>::type> //->运算符表示返回类型放在后方,返回类型为std::future类型,且由模板std::invoke_result<F, Args...>::type决定
	{
		using return_type = typename std::invoke_result<F, Args...>::type; //重命名返回类型

		auto task = std::make_shared<std::packaged_task<return_type()>>(
			std::bind(std::forward<F>(f), std::forward<Args>(args)...)
			);//构造一个函数对象智能指针,std::package_task可以包装一个异步返回的函数,std::forward可以进行完美转发。

		std::future<return_type> res = task->get_future(); //res保存线程返回值
		{
			std::unique_lock<std::mutex> lock(queue_mutex);

			// don't allow enqueueing after stopping the pool
			if (stop)
				throw std::runtime_error("enqueue on stopped ThreadPool");

			tasks.emplace([task]() { (*task)(); });
		}
		condition.notify_one(); //唤醒一个线程
		return res;
	}

	// the destructor joins all threads
	inline ThreadPool::~ThreadPool()
	{
		{
			std::unique_lock<std::mutex> lock(queue_mutex);
			stop = true;
		}
		condition.notify_all(); //唤醒所有线程
		for (auto& worker : workers)
		{
			if (worker.joinable()) {
				worker.join();
			}
		}
	}

	inline void ThreadPool::wait()
	{
		bool isRuning = true;
		while (isRuning) {
			isRuning = false;
			for (auto& status : this->status) {
				if (status) {
					isRuning = true;
					break;
				}
			}
			std::this_thread::sleep_for(std::chrono::milliseconds(100));
		}
	}

	inline void ThreadPool::join()
	{
		{
			std::unique_lock<std::mutex> lock(queue_mutex);
			stop = true;
		}
		condition.notify_all();
		for (auto& worker : workers)
		{
			if (worker.joinable()) {
				worker.join();
			}
		}
	}
}

进行了一定的完善,支持在C++20中使用,增加了join与wait函数。
用法:

// 创建一个4个工作线程的线程池
ThreadPool pool(4);

// 保存返回结果,std::future<int>
auto result = pool.enqueue([](int answer) { return answer; }, 42);

// 等待结果并输出
std::cout << result.get() << std::endl;
// 创建一个4个工作线程的线程池
ThreadPool pool(4);
// 创建一个future的vector批量保存结构
std::vector<std::future<int>> results;

for(int i = 0; i < 10; i++){
    // 保存返回结果,std::future<int>
    results.emplace_back(pool.enqueue([](int answer) { return answer*answer; }, i));
}

//输出结果
for(auto& res : results){
    std::cout << res.get() << std::endl;
}
results.clear();

GitHub: Thread Pool

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Thread Pool 线程池 的相关文章

随机推荐

  • Springboot 集成Druid

    Springboot 集成Druid Druid是Java语言中最好的数据库连接池 Druid能够提供强大的监控和扩展功能 本篇主要讲解一下 Springboot中如何集成 Druid 1 添加Druid依赖
  • QML 信号 槽 传递参数

    详细的C QML 的信号与槽 初学者教程 请参考该文 QML 中信号和槽之间传递参数并不需要在连接的时候特别声明 只需要将其关联在一起即可 main qml import QtQuick 2 13 import QtQuick Contro
  • flask web 01--基础入门案例

    flask web 01 基础入门案例 1 介绍 2 功能说明 3 源码分析 3 1 功能 3 2 源码 3 3 测试结果 4 注意事项 5 说明 1 介绍 最近由于需要 开始补了一些python web相关的技能 考虑到高效和简洁 就补了
  • Spring Boot 依赖注入

    Spring Boot 依赖注入 SpringBoot的实现方式基本都是通过注解实现的 跟Spring注解注入差不多 相关的常见注解有Autowired Resource Qualifier Service Controller Repos
  • 个人简历小程序

    为了熟练掌握微信小程序开发的一些基本技巧 熟悉微信小程序开发流程 特此 运用所学知识 做了一个个人简历小程序 效果图如下 代码如下 index js const app getApp Page data userInfo hasUserIn
  • Unity制作自定义消息提示框

    工作中使用软件经常会出现各种提示框 确定XXXXXX吗 选项一般是三个 是 否 取消 今天心血来潮 于是也尝试自己用unity制作一个简单的消息提示框 准备工作 首先需要搭建一个最简单的消息框界面 然后开始编写编写一个单例模式类 专门用来管
  • MySQL高级篇——事务

    一 简介 1 什么是事务 事务是由一个或多个sql语句组成一个最小的不可再分的工作单元 里面的内容要么都执行成功 要么都不成功 2 事务的ACID特性 原子性 atomicity 事务是一个不可分割的工作单元 要么全部提交 要么全部失败回滚
  • PhotoShop 之图层蒙版

    分黑色蒙版和白色蒙版 使用效果不同 黑色蒙版是表现 隐藏 当前图层的内容 是透明的 可以看到背景图层的内容 透明 白色蒙版是表示 显示 当前图层的内容 使用黑色画笔涂抹可以显示出背景图层的样子 不透明 总之记住一句话 黑色透明 白色不透明
  • 大学最应该学习的 5 门课, 毕业后大厂 Offer 直接拿到手软!

    时间如白驹过隙 我竟然已经是一名拥有 13 年编程经验的老油条了 有些自豪 因为自己从大一就开始学习的 Java 语言依然坚挺 几乎是编程语言中的霸主了 但也有些遗憾 大学的时候没有把这些计算机基础课程学好 有些甚至没有学 导致工作后有很长
  • C语言实现RGB packet格式转YUV(NV21)格式

    前言 此函数用于RGB packet R G B R G B 格式转YUV的NV21格式 保存NV21图像的内存由外部申请并传入 函数的具体实现原理在这里不做介绍 对RGB格式和YUV格式不熟悉的请自行查阅资料 该函数可直接拷贝过去使用 代
  • 智能排水解决方案,设备实时监控+预测分析,真正实现“无人值守”!

    什么是矿山排水 随着煤炭开采深度不断增加 地质条件也愈加复杂 井下综采作业会面临越来越高的渗水风险 为确保井下综采作业安全 需要设置大量排水系统 在矿山建设和生产过程中排除进入矿山的地下水和地表水 矿井排水系统是保障煤矿安全生产的关键环节
  • 求助关于speedtest中ookla跨域问题

    报错导致speedtest一直未上线 系统为windows 2012 求助给出合适的解决办法
  • Unity2D敌人/怪物AI控制 第一期

    AI 原地巡逻自动攻击型 AI会在横版地图上向左向右移动 移动一段距离后会原地停止移动 等待一段时间后 会随机向左或向右移动 以此循环 其中AI移动速度 移动时间 停留时间均可以自行调控 当人物进入怪物攻击范围后 会自动面向人物进行攻击 攻
  • 网络编程(20)—— 广播的编程实现

    一 广播和多播的区别 广播是向同一网络中所有主机传播数据的一种方式 它和多播的主要区别在于 1 从范围上来说 多播即使在跨越不同网络的情况下 只要加入多播组就能接受数据 而广播只能向同一网络中的主机传输数据 这种网络可以是发送者所在的网络
  • 一次完整的渗透测试流程,网站渗透存在哪些漏洞和隐藏的风险?

    渗透测试 渗透测试就是利用我们所掌握的渗透知识 对网站进行一步一步的渗透 发现其中存在的漏洞和隐藏的风险 然后撰写一篇测试报告 提供给我们的客户 客户根据我们撰写的测试报告 对网站进行漏洞修补 以防止黑客的入侵 渗透测试的前提是我们得经过用
  • LNK2005: _DllMain@12 already defined in LIBCMTD.lib(dllmain.obj)

    今天使用VS2003创建一个MFC 的dll工程时 出现以下错误 VPR error LNK2005 DllMain 12 already defined in LIBCMTD lib dllmain obj VPR error LNK20
  • 安全形势不容乐观 信息安全调查揭露五大误区

    日前 信息周刊 研究部和国际商业机器公司 IBM 合作进行了2008年 中国信息安全调查 这也是 信息周刊 以 11 年全球信息安全调查为基础 在中国开展的第四次安全调查 调查结果全面揭示了当前不容乐观的安全形势 对首席信息官 CIO 而言
  • Arthas 常用命令

    官方文档 Arthas Install Arthas 3 5 5 文档 安装 Arthas 是阿里开源的一款 linux mac 上性能问题排查工具 文档链接 Arthas 用户文档 Arthas 3 5 5 文档 这个里边具体步骤和指令讲
  • 数据链路层:ARP协议详解(绝对经典)

    ARP协议定义 地址解析协议 工作在数据链路层 在本层和硬件接口联系 同时向上层提供服务 IP数据包常通过以太网发送 以太网设备不识别32位IP地址 他们是以48位以太网地址传输以太网数据包的 因此需要IP转化为以太网目的地址 ARP协议用
  • Thread Pool 线程池

    Thread Pool 本文基于经典的99行代码 稍加修改使其支持C 20 并增加了wait与join功能 pragma once include