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 模式有代表性,能把(任务队列,超时,系统中断信号)等结合起来…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
