C++标准学习--多线程

在以往多线程的实现的时候,都是自己去亲自创建线程,采用特殊flag 及锁控制线程的运转状态。这无可厚非,但又似乎有重复造轮子的嫌疑。最近发现了一个线程池的轮子,很不错,ZZ一下。

C++多线程+线程池(全详解) - 知乎 (zhihu.com)

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。

线程池

 安全工作队列实现

#include<queue>
#include<mutex>
template<typename T>
class safequeue
{private:std::queue<T>m_queue;std::mutex m_mutex;public:safequeue();safequeue(safequeue &&other);~safequeue();bool empty(){std::unique_lock<std::mutex>lock(m_mutex);return m_queue.empty();}int  size(){std::unique_lock<std::mutex>lock(m_mutex);return m_queue.size();}void enqueue(T &t){std::unique_lock<std::mutex>lock(m_mutex);m_queue.emplace(&t);}bool dequeue(T &t){std::unique_lock<std::mutex>lock(m_mutex);if(m_queue.empty())return false;t=std::move(m_queue.front());m_queue.pop();return true;}
};

 线程池

#include<functional>
#include<mutex>
#include<vector>
#include<D:\code\c++project\C++\src\project\safequeue.c++>
#include<thread>
#include<condition_variable>
#include<future>
class threadpool
{private:class threadworker{private:int m_id;threadpool *m_pool;public:threadworker(const int id,threadpool *pool):m_id(id),m_pool(pool){}void operator()()//重载操作{std::function<void()> func;bool dequeued;if(!m_pool->m_shutdown){std::unique_lock<std::mutex>lock(m_pool->m_conditional_mutex);if(m_pool->m_queue.empty()){m_pool->m_conditional_lock.wait(lock);}dequeued=m_pool->m_queue.dequeue(func);}if(dequeued){func();}}}; bool m_shutdown;std::vector<std::thread> m_threads;safequeue<std::function<void()>> m_queue;std::mutex m_conditional_mutex;std::condition_variable m_conditional_lock;public:threadpool(const int n_threads=4):m_threads(std::vector<std::thread>(n_threads)),m_shutdown(false){}threadpool(const threadpool &)=delete;threadpool(threadpool &&)=delete;threadpool &operator=(const threadpool &)=delete;threadpool &&operator=(threadpool &&)=delete;void init()//初始化分配线程{for(int i=0;i<m_threads.size();i++){m_threads.at(i)=std::thread(threadworker(i,this));}}void shutdown()//关闭线程{m_shutdown=true;m_conditional_lock.notify_all();for(int i=0;i<m_threads.size();i++){if( m_threads.at(i).joinable()){m_threads.at(i).join();}}}template<typename F,typename... Args>auto submit(F &&f,Args &&...args)->std::future<decltype(f(args...))>{
//这段C++代码定义了一个名为`submit`的函数模板。它接受一个可调用对象`f`和一系列参数`args`。//函数模板使用了右值引用和可变参数模板的特性。`F &&f`表示对可调用对象`f`进行右值引用,`Args &&...args`表示可变数量的参数`args`,它们都是右值引用。//返回类型使用了`std::future<decltype(f(args...))>`,表示返回一个`std::future`对象,该对象的类型是通过调用`f(args...)`来推断的。//函数体中没有具体的实现,因此代码块中的大括号是空的。这意味着在使用这个函数模板时,需要根据具体的需求来提供实现。//这段代码的目的是定义一个通用的函数模板,用于提交任务并返回一个`std::future`对象,以便在将来某个时刻获取任务的结果。通过使用右值引用和可变参数模板,可以接受不同类型的可调用对象和参数,并返回相应的`std::future`对象。//总而言之,这段代码定义了一个通用的函数模板`submit`,用于提交任务并返回一个`std::future`对象,以便在将来获取任务的结果。具体的实现需要根据具体的需求来提供。std::function<decltype(f(args...))()> func=std::bind(std::forward<F>(f),std::forward<Args>(args)...);//forward为完美转发auto task_ptr=std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);std::function<void()>task=[task_ptr](){(*task_ptr)();};
//这段C++代码创建了一个`std::function`对象`wrapper_func`,并使用Lambda表达式作为其可调用对象。//Lambda表达式`[task_ptr]() { (*task_ptr)(); }`定义了一个匿名函数,没有参数,返回类型为`void`。在函数体中,通过解引用`task_ptr`,调用了指针所指向的可调用对象。//这个Lambda表达式被用作初始化`std::function<void()>`对象`wrapper_func`,因此`wrapper_func`成为了一个可调用对象,可以像函数一样被调用。//这段代码的作用是将一个指针`task_ptr`所指向的可调用对象进行封装,并通过`std::function`对象`wrapper_func`来调用该可调用对象。通过这种方式,可以将一个具体的任务对象包装成一个可调用对象,并进行进一步的处理和调用。//总而言之,这段代码创建了一个`std::function`对象,并使用Lambda表达式将一个指针所指向的可调用对象进行封装。这样可以通过`std::function`对象来调用该可调用对象,并进行进一步的处理。m_queue.enqueue(task);m_conditional_lock.notify_one();return task_ptr->get_future();}
};

线程池测试

#include<iostream>
#include<D:\code\c++project\C++\src\project\threadpool.c++>
#include <random>std::random_device rd; // 真实随机数产生器std::mt19937 mt(rd()); //生成计算随机数mtstd::uniform_int_distribution<int> dist(-1000, 1000); //生成-1000到1000之间的离散均匀分布数auto rnd = std::bind(dist, mt);// 设置线程睡眠时间
void simulate_hard_computation()
{std::this_thread::sleep_for(std::chrono::milliseconds(2000 + rnd()));
}// 添加两个数字的简单函数并打印结果
void multiply(const int a, const int b)
{simulate_hard_computation();const int res = a * b;std::cout << a << " * " << b << " = " << res << std::endl;
}// 添加并输出结果
void multiply_output(int &out, const int a, const int b)
{simulate_hard_computation();out = a * b;std::cout << a << " * " << b << " = " << out << std::endl;
}// 结果返回
int multiply_return(const int a, const int b)
{simulate_hard_computation();const int res = a * b;std::cout << a << " * " << b << " = " << res << std::endl;return res;
}void example()
{// 创建3个线程的线程池threadpool pool(3);// 初始化线程池pool.init();// 提交乘法操作,总共30个for (int i = 1; i <= 3; ++i)for (int j = 1; j <= 10; ++j){pool.submit(multiply, i, j);}// 使用ref传递的输出参数提交函数int output_ref;auto future1 = pool.submit(multiply_output, std::ref(output_ref), 5, 6);// 等待乘法输出完成future1.get();std::cout << "Last operation result is equals to " << output_ref << std::endl;// 使用return参数提交函数auto future2 = pool.submit(multiply_return, 5, 3);// 等待乘法输出完成int res = future2.get();std::cout << "Last operation result is equals to " << res << std::endl;// 关闭线程池pool.shutdown();
}int main()
{example();return 0;
}

 该轮子通过

std::thread(threadworker(i,this))

来创建线程,work后的()遭到重载,进行等待信号及dequeue动作,来调用queue内进入的func。而submit的作用是将func们压入queue。并提示线程们有新压入进来的func。 

线程池内有处理函数,有处理结果查询函数,二者怎么协调动作以达到最佳整体运转性能是需要考虑的地方。这里的线程池轮子是将每个功能块的线程开辟统一到了一个模板下,每个功能块只需要注册自己的操作函数即可,具有一定的简洁性。不足之处是每个功能块需要共享模板的实现和声明。总之是具有一定的进步性的。

使用这个轮子的另一个难度地方是,如果func内需要调用功能块里的作用域的函数时候,是需要一些额外传值的。这在设计的时候是需要考虑到的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/616947.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

免费学习鸿蒙(HarmonyOS)开发,一些地址分享

HarmonyOS万物互联&#xff0c;从华为一系列的操作来看已经与iOS、Android形成三足鼎立之势了。 根据《澎湃新闻》的报道&#xff0c;已有23所985高校和46所211高校加入了鸿蒙班的行列&#xff0c;合计达到了69所国内一流高校。通过鸿蒙班的设立&#xff0c;高校可以为学生提供…

【Java】正则表达式( Pattern 和 Matcher 类)

快速入门 Java 提供了 java.util.regex 包&#xff0c;它包含了 Pattern 和 Matcher 类&#xff0c;用于处理正则表达式的匹配操作。 java.util.regex 包主要包括以下三个类&#xff1a; Pattern 类&#xff1a; pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共…

从Scroll怒喷社区用户事件,看L2龙头ZKFair的做事格局

这两天&#xff0c;随着美国SEC正式批准所有11只比特币现货ETF的消息公布&#xff0c;吸引了传统主流增量资金的入场&#xff0c;比特币多头一举将比特币干到了48000刀的位置&#xff0c;并随时向着前高发起了冲击。比特币的强势带动了其他加密资产的保障&#xff0c;整个加密市…

面试常见问题回答干货

1、请你自我介绍一下你自己&#xff1f; 回答提示&#xff1a;一般人回答这个问题过于平常&#xff0c;只说姓名、年龄、爱好、工作经验&#xff0c;这些在简历上都有。其实&#xff0c;企业最希望知道的是求职者能否胜任工作&#xff0c;包括&#xff1a;最强的技能、最深入研…

基于elementUI封装的带复选框el-checkbox的下拉多选el-select组件

效果图&#xff1a; 组件&#xff1a;MultipleSelect.vue <template><el-select v-model"selectValues" v-bind"$attrs" v-on"listeners" multiple placeholder"请选择" style"width: 50%" change"changeSel…

kylin4.0.3升级问题

话接前文&#xff1a; kylin升级(3.0.1-&#xff1e;kylin-4.0.3)-CSDN博客文章浏览阅读941次&#xff0c;点赞29次&#xff0c;收藏12次。原本的cube太多了&#xff0c;换其他OLAP数据库太麻烦。相比之下&#xff0c;升级是一个很好的选择&#xff08;官网有说明内存降低和构…

玩转“资本腾挪之术”,戴震的能链版图扩至何处?

在当下经济恢复发展缓慢&#xff0c;以及消费市场需求疲软的大环境下&#xff0c;企业融资变得越来越艰难。 但即便如此&#xff0c;仍有个别企业却独得众多资本大鳄青睐&#xff0c;这家企业就是能链集团。据不完全统计&#xff0c;2016年成立至今&#xff0c;能链集团已经完…

一站式搞定文案生成、违规检测、一键分发的全流程

随着社交媒体的不断发展&#xff0c;越来越多的企业开始布局新媒体矩阵&#xff0c;从集团总部到区域门店、个人销售&#xff0c;从全品类到细分垂直类目、从单一平台到多平台&#xff0c;试图让品牌影响力覆盖更广泛群体&#xff0c; 当然&#xff0c;随之而来的&#xff0c;如…

砥砺深耕,历伴童行 ——南阳人人社工2022年度未保项目结项回顾

未成年人的健康成长&#xff0c;关乎国家未来和民族兴旺。2023年&#xff0c;南阳市人人社会工作服务中心在南阳市民政局的指导下&#xff0c;坚持党建引领未成年人保护服务&#xff0c;紧紧围绕“保权益&#xff0c;促发展&#xff0c;育新人”的使命任务&#xff0c;以最有利…

蓝桥杯单片机组备赛——蜂鸣器和继电器的基本控制

文章目录 一、蜂鸣器和继电器电路介绍二、题目与答案2.1 题目2.2 答案2.3 重点函数解析 一、蜂鸣器和继电器电路介绍 可以发现两个电路一端都接着VCC&#xff0c;所以我们只要给另一端接上低电平就可以让蜂鸣器和继电器进行工作。与操作LED类似&#xff0c;只不过换了一个74HC5…

爬虫之使用代理

爬虫—使用代理 1. 为什么使用代理 1.1 让服务器以为不是同一个客户端在请求 1.2 防止我们的真实地址被泄漏&#xff0c;防止被追究 2. 理解使用代理的过程 3. 理解正向代理和反向代理的区别 通过上图可以看出&#xff1a; 正向代理&#xff1a;对于浏览器知道服务器的真实…

vue前端开发自学demo,父子组件之间传递数据demo2

vue前端开发自学demo,父子组件之间传递数据demo2!实际上&#xff0c;组件之间传递数据的&#xff0c;数据类型&#xff0c;是可以多种多样的&#xff0c;下面为大家展示几个常见的数据类型&#xff0c;比如数字类型&#xff0c;数组类型&#xff0c;对象类型。 代码如下所示&a…

Linux Debian12系统gnome桌面环境默认提供截屏截图工具gnome-screenshot

一、简介&#xff1a; 在Debian12中系统gnome桌面环境默认提供一个截图捕获工具screenshot,可以自定义区域截图、屏幕截图、窗口截图和录制视频&#xff0c;截图默认保存在“~/图片/截图”路径下。 可以在应用程序中搜索screenshot,如下图&#xff1a; 也可以在桌面右上角找到…

算法进阶——按之字形顺序打印二叉树

题目 给定一个二叉树&#xff0c;返回该二叉树的之字形层序遍历&#xff0c;&#xff08;第一层从左向右&#xff0c;下一层从右向左&#xff0c;一直这样交替&#xff09;。 数据范围&#xff1a;0≤n≤1500,树上每个节点的val满足∣val∣<1500 要求&#xff1a;空间复杂…

物理机搭建hive

一、修改Hadoop配置 修改core-site.xml 配置yarn-site.xml 分发文件&#xff0c;然后重启集群 二、 Hive解压安装 上传文件 添加hive环境便量&#xff0c;source生效 启动高可用集群&#xff0c;启动hive 三、配置mysql元数据库 检查当前系统是否安装过Mysql&#xf…

AJAX入门到实战,学习前端框架前必会的(ajax+node.js+webpack+git)(七)

08.什么是模块化&#xff1f; CommonJS 标准 09.ECMAScript 标准 - 默认导出和导入 10.ECMAScript 标准 - 命名导出和导入 11.包的概念 实操&#xff1a; server.js utils/lib/index.js utils/package.json 12.npm - 软件包管理器 13.npm - 安装所有依赖 从别处&#xff08;网…

HarmonyOS应用开发学习笔记 UIAbility组件与UI的数据同步 EventHub、globalThis

1、 HarmoryOS Ability页面的生命周期 2、 Component自定义组件 3、HarmonyOS 应用开发学习笔记 ets组件生命周期 4、HarmonyOS 应用开发学习笔记 ets组件样式定义 Styles装饰器&#xff1a;定义组件重用样式 Extend装饰器&#xff1a;定义扩展组件样式 5、HarmonyOS 应用开发…

计算机缺失msvcp140.dll的修复教程,教你快速解决dll问题

“针对计算机系统中出现的msvcp140.dll文件丢失问题&#xff0c;小编将详细阐述一系列有效的解决方法。首先&#xff0c;msvcp140.dll是Microsoft Visual C Redistributable Package中的一个关键动态链接库文件&#xff0c;对于许多应用程序的正常运行至关重要。当系统提示该文…

RS触发器

转自&#xff1a;【基础】RS触发器_两个或非门构成rs触发器-CSDN博客 RS触发器为什么能 “保持上一状态” 触发器就是在常规的门电路的基础上加入了反馈&#xff0c;这样触发器就实现了存储数据的功能。这也是上面章节 “RS触发器实验” 的 RS触发器特征表 中第3条 “保持上一…

线性分解模型(LDM)的扩展方法——分析稀疏数据里的微生物组存在或缺失关联

谷禾健康 生态学家在分析微生物组和感兴趣的协变量(如临床结果或环境因素)之间的关联时&#xff0c;经常以两种方式查看物种分类计数数据。 一种是将计数视为定量的(即作为相对丰度数据进行分析)&#xff1b;另一种是将计数数据离散化&#xff0c;只表明一个分类单元在样本中是…