c++源码阅读__ThreadPool__正文阅读
一. 简介
本章我们开始阅读c++ git 高星开源项目ThreadPool, 这是一个纯c++的线程池项目, 并且代码量极小, 非常适合新手阅读
git地址: progschj / ThreadPool
二. 前提知识
为了面对不同读者对c++掌握情况不同的情况, 这里我会将基本上稍微值得一说的前提知识点, 全部专门写成一篇博客, 同学们在阅读本篇之前, 可以先去阅读前提知识部分
c++源码阅读__ThreadPool__前提基础部分
还有线程的一些基础知识
C++ 多线程 菜鸟教程
三. 源码:
因为源码时c++11的, 所以我们如果用最新的标准是跑不起来的, 所以这里我在下面源码部分把能用最新标准跑的版本的代码贴了出来
修改的地方只有一处, 如下
返回类型的推导:
typename std::result_of<F(Args...)>::type
改为了
typename std::invoke_result<F, Args...>::type
由于此项目比较小, 所以我们直接把代码全部贴出来, 并且在代码中, 用注释附上讲解
#ifndef THREAD_POOL_H
#define THREAD_POOL_H#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
#include <iostream>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();
private:// 线程队列的 vectorstd::vector< std::thread > workers;// 任务列表, 在前提知识里, 已经说明了, 通过lambda和bind将方法斗包装为void()类型的// 至于任务的返回值, 通过future实现std::queue< std::function<void()> > tasks;// synchronizationstd::mutex queue_mutex;// 主线程通知子线程的工具std::condition_variable condition;bool stop;
};// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads): stop(false)
{for (size_t i = 0; i < threads; ++i)// 这了workers.emplace_back([](){}) 为什么能够直接生成Thread呢?// 因为emplace_back 和 push_back不同, emplace_back传入的参数可以是 构造函数的 参数, // 所以这里写全了 应该是类似下面的代码// workers.emplace_back( Thread( [](){} ) )workers.emplace_back([this]{for (;;){std::function<void()> task;{// 这里单独{}开一个域, 是因为unique_lock生效的范围是当前作用域std::unique_lock<std::mutex> lock(this->queue_mutex);// 这里condition_variable的wait, 等待一个notifythis->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });if (this->stop && this->tasks.empty()){std::cout << std::this_thread::get_id() << std::endl;return;}task = std::move(this->tasks.front());this->tasks.pop();}// 脱离了lock域, 真正执行方法的地方, 还是多线程的, 如果写在上面的lock域里// 那就变成 "单线程" 了task();}});
}// 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>
{using return_type = typename std::invoke_result<F, Args...>::type;// 为什么要用shared_ptr? 因为后面使用queue.emplace, 会将task传递到queue中,// 当离开此方法时, task因为离开作用域, 会销毁, 而shared_ptr则不会销毁, 而是引用计数-1auto task = std::make_shared< std::packaged_task<return_type()> >(std::bind(std::forward<F>(f), std::forward<Args>(args)...));// 并且这里要注意, task不是packaged_task, 而是shared_ptrstd::future<return_type> res = task->get_future();{// 为什么这里要开一个单独的作用域呢? 因为这里unique_lock是以作用域进行lock的std::unique_lock<std::mutex> lock(queue_mutex);// don't allow enqueueing after stopping the poolif (stop)throw std::runtime_error("enqueue on stopped ThreadPool");// 这里通过lambda, 及那个task包装为一个void()的函数, 里面的*task是shared_ptr指针指向的packaged_tasktasks.emplace([task]() { (*task)(); });}// 任务装入queue后, 通知子线程执行condition.notify_one();return res;
}// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{{// 同样的原因, unique_lock的生效范围是当前作用域std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();// 执行到notify_all的时候, 在线程的方法里, 实际上已经return了, 这里循环join// 是为了让主线程结束的时候, 子线程也随之结束for (std::thread& worker : workers)worker.join();
}#endif
调用方法
#include "ThreadPool.h"
using namespace std;int main()
{// 线程数量为4的鲜橙汁ThreadPool pool(4);// 8个返回类型都是int的future数组std::vector< std::future<int> > results;for (int i = 0; i < 8; ++i) {auto f = [i]() {std::this_thread::sleep_for(std::chrono::seconds(1));return i;};// 设置任务, 并将返回的future放入results里results.emplace_back(pool.enqueue(f));}// 循环打印结果for (auto&& result : results)std::cout << result.get() << endl;// 设置一个string(const char*, const char*)的方法, 并获取返回的future<string>std::future<string> f = pool.enqueue([](const char* s1, const char* s2) {return string(s1) + s2;}, "hello ", "world");cout << f.get() << endl;std::cout << std::endl;return 0;
}
执行结果

总结
技巧:
- 通过lambda 或者 bind 来改变函数的参数个数
- 通过构造 packaged_task来改变返回值传递的方式, 方便将方法统一放入vector, 并且是异步执行的
- 通过lambda来改变函数返回值类型
相关文章:
c++源码阅读__ThreadPool__正文阅读
一. 简介 本章我们开始阅读c git 高星开源项目ThreadPool, 这是一个纯c的线程池项目, 并且代码量极小, 非常适合新手阅读 git地址: progschj / ThreadPool 二. 前提知识 为了面对不同读者对c掌握情况不同的情况, 这里我会将基本上稍微值得一说的前提知识点, 全部专门写成一篇…...
关于ES的查询
查询结果那么多字段都是什么? 为什么会提到这个问题呢,因为默认ES查询的结果会有很多信息,我们可能并不希望要那么多数据,所以你需要了解这些字段都表示什么,并正确的返回和使用它们。 took– Elasticsearch 运行查询…...
数据结构初识
目录 1.初识 2.时间复杂度 常见时间复杂度举例: 3.空间复杂度 4.包装类&简单认识泛型 4.1装箱和拆箱 5.泛型 6.泛型的上界 7.泛型方法 8.List接口 1.初识 1.多画图 2.多思考 3.多写代码 4.多做题 牛客网-题库/在线编程/剑指offer 算法篇:…...
保存数据到Oracle时报错ORA-17004: 列类型无效: 1111
1、问题描述: 关键信息:Mybatis;Oracle (1)保存信息到Oracle时报错: Caused by: org.apache.ibatis.type.TypeException: Error setting null for parameter #10 with JdbcType OTHER . Try setting a dif…...
Excel——宏教程(1)
Microsoft excel是一款功能非常强大的电子表格软件。它可以轻松地完成数据的各类数学运算,并用各种二维或三维图形形象地表示出来,从而大大简化了数据的处理工作。但若仅利用excel的常用功能来处理较复杂的数据,可能仍需进行大量的人工操作。…...
论文浅尝 | MindMap:知识图谱提示激发大型语言模型中的思维图(ACL2024)
笔记整理:和东顺,天津大学硕士,研究方向为软件缺陷分析 论文链接:https://aclanthology.org/2024.acl-long.558/ 发表会议:ACL 2024 1. 动机 虽然大语言模型(LLMs)已经在自然语言理解和生成任务…...
第6章:TDengine 标签索引和删除数据
TDengine 标签索引和删除数据 目标 掌握标签索引的创建、删除掌握超表、子表创建以及数据删除删除数据 删除数据是 TDengine 提供的根据指定时间段删除指定表或超级表中数据记录的功能,方便用户清理由于设备故障等原因产生的异常数据。 注意:删除数据并不会立即释放该表所…...
【微软:多模态基础模型】(5)多模态大模型:通过LLM训练
欢迎关注[【youcans的AGI学习笔记】](https://blog.csdn.net/youcans/category_12244543.html)原创作品 【微软:多模态基础模型】(1)从专家到通用助手 【微软:多模态基础模型】(2)视觉理解 【微…...
海外带云仓多语言商城源码,多语言多商家云仓一键代发商城
新增海外仓,云仓国际供应链系统,商家可登陆云仓进行批量发货 商城修复了一些bug以及增加了订单数字提示,优化加载速度,二开了一些细微功能 基于 PHP Laravel 框架开发的一款 Web 商城系统。 1.前端多国语言自由切换,…...
android:taskAffinity 对Activity退出时跳转的影响
android:taskAffinity 对Activity跳转的影响 概述taskAffinity 的工作机制taskAffinity对 Activity 跳转的影响一个实际的开发问题总结参考 概述 在 Android 开发中,任务栈(Task)是一个核心概念。它决定了应用程序的 Activity 如何相互交互以…...
Apache Dolphinscheduler数据质量源码分析
Apache DolphinScheduler 是一个分布式、易扩展的可视化数据工作流任务调度系统,广泛应用于数据调度和处理领域。 在大规模数据工程项目中,数据质量的管理至关重要,而 DolphinScheduler 也提供了数据质量检查的计算能力。本文将对 Apache Do…...
solana链上智能合约开发案例一则
环境搭建 安装Solana CLI:Solana CLI是开发Solana应用的基础工具。你可以通过官方文档提供的安装步骤,在本地环境中安装适合你操作系统的Solana CLI版本。安装完成后,使用命令行工具进行配置,例如设置网络环境(如开发网…...
使用 PyTorch 实现 ZFNet 进行 MNIST 图像分类
在本篇博客中,我们将通过两个主要部分来演示如何使用 PyTorch 实现 ZFNet,并在 MNIST 数据集上进行训练和测试。ZFNet(ZFNet)是基于卷积神经网络(CNN)的图像分类模型,广泛用于图像识别任务。 环…...
车轮上的科技:Spring Boot汽车新闻集散地
1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理汽车资讯网站的相关信息成为必然。开发合适…...
IDEA2023 SpringBoot整合Web开发(二)
一、SpringBoot介绍 由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。SpringBoot提供了一种新的编程范式,可以更加快速便捷…...
国产三维CAD 2025新动向:推进MBD模式,联通企业设计-制造数据
本文为CAD芯智库原创整理,未经允许请勿复制、转载! 上一篇文章阿芯分享了影响企业数字化转型的「MBD」是什么、对企业优化产品设计流程有何价值——这也是国产三维CAD软件中望3D 2024发布会上,胡其登先生(中望软件产品规划与GTM中…...
ubuntu 之 安装mysql8
安装 # 如果 ubuntu 版本 > 20.04 则不用执行 wget 这步 wget https://dev.mysql.com/get/mysql-apt-config_0.8.12-1_all.debsudo apt-get updatesudo apt-get install mysql-server mysql-client 安装过程中如果没有提示输入密码 sudo cat /etc/mysql/debian.cnf # 查…...
Flink Lookup Join(维表 Join)
Lookup Join 定义(支持 Batch\Streaming) Lookup Join 其实就是维表 Join,比如拿离线数仓来说,常常会有用户画像,设备画像等数据,而对应到实时数仓场景中,这种实时获取外部缓存的 Join 就叫做维…...
Elasticsearch retrievers 通常与 Elasticsearch 8.16.0 一起正式发布!
作者:来自 Elastic Panagiotis Bailis Elasticsearch 检索器经过了重大改进,现在可供所有人使用。了解其架构和用例。 在这篇博文中,我们将再次深入探讨检索器(retrievers)。我们已经在之前的博文中讨论过它们…...
【并发模式】Go 常见并发模式实现Runner、Pool、Work
通过并发编程在 Go 程序中实现的3种常见的并发模式。 参考:https://cloud.tencent.com/developer/article/1720733 1、Runner 定时任务 Runner 模式有代表性,能把(任务队列,超时,系统中断信号)等结合起来…...
TinyMCE 5插件开发实战:手把手教你定制首行缩进功能(Vue版)
TinyMCE 5插件开发实战:手把手教你定制首行缩进功能(Vue版) 在内容创作领域,富文本编辑器的灵活性和扩展性往往决定了最终的用户体验。TinyMCE作为一款广受欢迎的富文本编辑器,其插件系统为开发者提供了无限可能。本文…...
6大终极方案!WarcraftHelper全方位解决魔兽争霸III在Win10/11兼容性难题
6大终极方案!WarcraftHelper全方位解决魔兽争霸III在Win10/11兼容性难题 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 经典游戏魔兽争霸…...
果园灌溉施肥控制系统改造之西门子 S7 - 1200 PLC 实战
果园灌溉施肥控制系统改3 西门子s7-1200plc程序博途v16,带 选型表 io表接线图CAD和运行效果视频最近搞了个果园灌溉施肥控制系统的改造项目,用的是西门子 S7 - 1200 PLC,编程软件是博途 V16,这过程还挺有意思,跟大家…...
Medusa小程序:微信、支付宝等小程序平台对接的完整指南
Medusa小程序:微信、支付宝等小程序平台对接的完整指南 【免费下载链接】medusa 项目提供了构建数字商务所需的组件和服务,旨在简化和加速电子商务平台的开发工作流程。 项目地址: https://gitcode.com/GitHub_Trending/me/medusa Medusa是一个强…...
避免这些坑!Unity2D界面转换中常见的动画事件处理问题及解决方案
避免这些坑!Unity2D界面转换中常见的动画事件处理问题及解决方案 在Unity2D游戏开发中,界面转换是提升用户体验的关键环节。一个流畅的淡入淡出效果能让场景切换更加自然,但很多开发者在实际操作中常会遇到动画事件不触发、协程执行异常等问题…...
BLE5.1 与蓝牙Mesh 在手环数字车钥匙中的角色与体验升级
可穿戴数字车钥匙把传统实体钥匙的能力收敛到手环、手表等贴身设备上,通过近距无线链路与车载控制器或专用通信单元交互,支持解闭锁、启动、迎宾等操作。典型实现会组合 低功耗蓝牙(BLE) 做常在线链路与距离感知,并以 …...
《QGIS快速入门与应用基础》240:指北针旋转与大小调整
作者:翰墨之道,毕业于国际知名大学空间信息与计算机专业,获硕士学位,现任国内时空智能领域资深专家、CSDN知名技术博主。多年来深耕地理信息与时空智能核心技术研发,精通 QGIS、GrassGIS、OSG、OsgEarth、UE、Cesium、OpenLayers、Leaflet、MapBox 等主流工具与框架,兼具…...
Comsol多重法诺共振拟合:探索与实践
comsol多重法诺共振拟合。 在光学与光子学领域,多重法诺共振现象一直是研究的热点。而Comsol作为一款强大的多物理场仿真软件,为我们研究多重法诺共振提供了有力的工具,尤其是其中的拟合功能,能够帮助我们更精准地理解和分析这一…...
符号回归的工程化实践:基于深度学习的物理定律自动发现与工业部署
1. 符号回归:当深度学习遇见物理定律发现 第一次接触符号回归时,我被它的"反套路"特性惊艳到了——大多数深度学习模型都在努力变得更复杂,而它却在追求用最简单的数学公式解释世界。三年前我在化工厂做反应釜监控项目时࿰…...
ChatGPT文档上传安全指南:如何避免敏感信息泄露
ChatGPT文档上传安全指南:如何避免敏感信息泄露 在当今AI应用开发热潮中,将文档上传至ChatGPT等大语言模型进行内容分析、总结或问答,已成为提升工作效率的常见场景。然而,许多开发者在兴奋地集成这一强大功能时,往往…...
