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

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;
}

执行结果
在这里插入图片描述

总结

技巧:

  1. 通过lambda 或者 bind 来改变函数的参数个数
  2. 通过构造 packaged_task来改变返回值传递的方式, 方便将方法统一放入vector, 并且是异步执行的
  3. 通过lambda来改变函数返回值类型

相关文章:

c++源码阅读__ThreadPool__正文阅读

一. 简介 本章我们开始阅读c git 高星开源项目ThreadPool, 这是一个纯c的线程池项目, 并且代码量极小, 非常适合新手阅读 git地址: progschj / ThreadPool 二. 前提知识 为了面对不同读者对c掌握情况不同的情况, 这里我会将基本上稍微值得一说的前提知识点, 全部专门写成一篇…...

关于ES的查询

查询结果那么多字段都是什么&#xff1f; 为什么会提到这个问题呢&#xff0c;因为默认ES查询的结果会有很多信息&#xff0c;我们可能并不希望要那么多数据&#xff0c;所以你需要了解这些字段都表示什么&#xff0c;并正确的返回和使用它们。 took– Elasticsearch 运行查询…...

数据结构初识

目录 1.初识 2.时间复杂度 常见时间复杂度举例&#xff1a; 3.空间复杂度 4.包装类&简单认识泛型 4.1装箱和拆箱 5.泛型 6.泛型的上界 7.泛型方法 8.List接口 1.初识 1.多画图 2.多思考 3.多写代码 4.多做题 牛客网-题库/在线编程/剑指offer 算法篇&#xff1a…...

保存数据到Oracle时报错ORA-17004: 列类型无效: 1111

1、问题描述&#xff1a; 关键信息&#xff1a;Mybatis&#xff1b;Oracle &#xff08;1&#xff09;保存信息到Oracle时报错&#xff1a; Caused by: org.apache.ibatis.type.TypeException: Error setting null for parameter #10 with JdbcType OTHER . Try setting a dif…...

Excel——宏教程(1)

Microsoft excel是一款功能非常强大的电子表格软件。它可以轻松地完成数据的各类数学运算&#xff0c;并用各种二维或三维图形形象地表示出来&#xff0c;从而大大简化了数据的处理工作。但若仅利用excel的常用功能来处理较复杂的数据&#xff0c;可能仍需进行大量的人工操作。…...

论文浅尝 | MindMap:知识图谱提示激发大型语言模型中的思维图(ACL2024)

笔记整理&#xff1a;和东顺&#xff0c;天津大学硕士&#xff0c;研究方向为软件缺陷分析 论文链接&#xff1a;https://aclanthology.org/2024.acl-long.558/ 发表会议&#xff1a;ACL 2024 1. 动机 虽然大语言模型&#xff08;LLMs&#xff09;已经在自然语言理解和生成任务…...

第6章:TDengine 标签索引和删除数据

TDengine 标签索引和删除数据 目标 掌握标签索引的创建、删除掌握超表、子表创建以及数据删除删除数据 删除数据是 TDengine 提供的根据指定时间段删除指定表或超级表中数据记录的功能,方便用户清理由于设备故障等原因产生的异常数据。 注意:删除数据并不会立即释放该表所…...

【微软:多模态基础模型】(5)多模态大模型:通过LLM训练

欢迎关注[【youcans的AGI学习笔记】](https://blog.csdn.net/youcans/category_12244543.html&#xff09;原创作品 【微软&#xff1a;多模态基础模型】&#xff08;1&#xff09;从专家到通用助手 【微软&#xff1a;多模态基础模型】&#xff08;2&#xff09;视觉理解 【微…...

海外带云仓多语言商城源码,多语言多商家云仓一键代发商城

新增海外仓&#xff0c;云仓国际供应链系统&#xff0c;商家可登陆云仓进行批量发货 商城修复了一些bug以及增加了订单数字提示&#xff0c;优化加载速度&#xff0c;二开了一些细微功能 基于 PHP Laravel 框架开发的一款 Web 商城系统。 1.前端多国语言自由切换&#xff0c;…...

android:taskAffinity 对Activity退出时跳转的影响

android:taskAffinity 对Activity跳转的影响 概述taskAffinity 的工作机制taskAffinity对 Activity 跳转的影响一个实际的开发问题总结参考 概述 在 Android 开发中&#xff0c;任务栈&#xff08;Task&#xff09;是一个核心概念。它决定了应用程序的 Activity 如何相互交互以…...

Apache Dolphinscheduler数据质量源码分析

Apache DolphinScheduler 是一个分布式、易扩展的可视化数据工作流任务调度系统&#xff0c;广泛应用于数据调度和处理领域。 在大规模数据工程项目中&#xff0c;数据质量的管理至关重要&#xff0c;而 DolphinScheduler 也提供了数据质量检查的计算能力。本文将对 Apache Do…...

solana链上智能合约开发案例一则

环境搭建 安装Solana CLI&#xff1a;Solana CLI是开发Solana应用的基础工具。你可以通过官方文档提供的安装步骤&#xff0c;在本地环境中安装适合你操作系统的Solana CLI版本。安装完成后&#xff0c;使用命令行工具进行配置&#xff0c;例如设置网络环境&#xff08;如开发网…...

使用 PyTorch 实现 ZFNet 进行 MNIST 图像分类

在本篇博客中&#xff0c;我们将通过两个主要部分来演示如何使用 PyTorch 实现 ZFNet&#xff0c;并在 MNIST 数据集上进行训练和测试。ZFNet&#xff08;ZFNet&#xff09;是基于卷积神经网络&#xff08;CNN&#xff09;的图像分类模型&#xff0c;广泛用于图像识别任务。 环…...

车轮上的科技:Spring Boot汽车新闻集散地

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理汽车资讯网站的相关信息成为必然。开发合适…...

IDEA2023 SpringBoot整合Web开发(二)

一、SpringBoot介绍 由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。SpringBoot提供了一种新的编程范式&#xff0c;可以更加快速便捷…...

国产三维CAD 2025新动向:推进MBD模式,联通企业设计-制造数据

本文为CAD芯智库原创整理&#xff0c;未经允许请勿复制、转载&#xff01; 上一篇文章阿芯分享了影响企业数字化转型的「MBD」是什么、对企业优化产品设计流程有何价值——这也是国产三维CAD软件中望3D 2024发布会上&#xff0c;胡其登先生&#xff08;中望软件产品规划与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 定义&#xff08;支持 Batch\Streaming&#xff09; Lookup Join 其实就是维表 Join&#xff0c;比如拿离线数仓来说&#xff0c;常常会有用户画像&#xff0c;设备画像等数据&#xff0c;而对应到实时数仓场景中&#xff0c;这种实时获取外部缓存的 Join 就叫做维…...

Elasticsearch retrievers 通常与 Elasticsearch 8.16.0 一起正式发布!

作者&#xff1a;来自 Elastic Panagiotis Bailis Elasticsearch 检索器经过了重大改进&#xff0c;现在可供所有人使用。了解其架构和用例。 在这篇博文中&#xff0c;我们将再次深入探讨检索器&#xff08;retrievers&#xff09;。我们已经在之前的博文中讨论过它们&#xf…...

【并发模式】Go 常见并发模式实现Runner、Pool、Work

通过并发编程在 Go 程序中实现的3种常见的并发模式。 参考&#xff1a;https://cloud.tencent.com/developer/article/1720733 1、Runner 定时任务 Runner 模式有代表性&#xff0c;能把&#xff08;任务队列&#xff0c;超时&#xff0c;系统中断信号&#xff09;等结合起来…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...