一个线程池的理解

最近看到一个线程池,写的实在太好,于是想深入理解一下。原始代码出处:GitHub - Ahajha/CTPL: Modern and efficient C++ Thread Pool Library

由于平时的工程一般只支持到C++11,而拿到的代码应该是在C++20下才能编译通过,因此也做了一些修改,需要原始码的可去github上自行下载。

先出测试代码:

#include "ThreadPool.h"void func4444()
{printf("[%s]thread_id[%s]:func4444begin--------\n", current_time().c_str(),get_curr_thread_id().c_str());::Sleep(2000);printf("[%s]thread_id[%s]:func4444end..--------\n", current_time().c_str(), get_curr_thread_id().c_str());}void func5555(int v)
{printf("[%s]thread_id[%s]:func5555begin----参数: %d----\n", current_time().c_str(), get_curr_thread_id().c_str(), v);::Sleep(3000);printf("[%s]thread_id[%s]:func5555end..----参数: %d----\n", current_time().c_str(), get_curr_thread_id().c_str(), v);}int main()
{printf("[%s]main_thread_id:[%s]\n", current_time().c_str(), get_curr_thread_id().c_str());ctpl::thread_pool p;int intValue = 42;p.add_func("func4444", func4444);p.add_func("func5555", func5555, intValue);while (true){::Sleep(500);}
}

测试结果如下:

可以看到,加入到线程池的过程是在主线程中进行的,实际运行的都是在工作线程中完成。

关键代码

1、加入到任务队列

		template<typename F, typename... Args>void add_func(const char* name, F && f, Args&&... args){auto pck = std::make_shared<std::packaged_task<decltype(f(args...))()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));printf("[%s]thread_id[%s]:add_func: %s\n", current_time().c_str(), get_curr_thread_id().c_str(), name);this->push_func(std::make_unique<std::function<void()>>([pck, name] {if (strlen(name) > 0)printf("[%s]thread_id[%s]:sche_trace_func %s enter ...\n", current_time().c_str(), get_curr_thread_id().c_str(), name);(*pck)();if (strlen(name) > 0)printf("[%s]thraed_id[%s]:sche_trace_func %s exit .\n", current_time().c_str(), get_curr_thread_id().c_str(), name);}));}

pck是一个包装指针,(*pck)()会执行被包装任务中的函数 f,并传入参数 args...,因此(*pck)();实际就是执行f(args)

由于(*pck)()是在匿名函数,当执行this->push_func时,相当于把这个匿名函数存于func_tasks中,func_tasks是一个线程安全的队列。此时并未真正执行函数,只是暂存起来。注意,detail::atomic_queue<std::unique_ptr<std::function<void()>>> func_tasks;看到队列存储的函数类型是固定的,即无输入且无输出的函数类型,即我们这儿的匿名函数。

    void thread_pool::push_func(std::unique_ptr<std::function<void()>> &task){this->func_tasks.push(std::move(task));this->signal_cv.notify_one();}

当通过push_func将匿名函数加入进队列func_tasks后,会给工作线程发送通知,激活空闲的工作线程以执行此匿名函数,并从此匿名函数中执行对应的函数f(args)

2、从任务队列中取任务并执行

void thread_pool::emplace_thread(){this->stop_flags.emplace_back(std::make_shared<std::atomic<bool>>(false));// The main loop for the thread. Grabs a copy of the pointer// to the stop flag.const auto stop_flag = this->stop_flags.back();this->threads.emplace_back([this, stop_flag]{printf("[%s]thread_id:[%s]\n", current_time().c_str(), get_curr_thread_id().c_str());std::atomic<bool>& stop = *stop_flag;// Used to store new tasks.std::unique_ptr<std::function<void()>> task;// True if 'task' currently has a runnable task in it.auto has_new_task = this->func_tasks.pop(task);while (true){// If there is a task to runwhile (has_new_task){// Run the task(*task)();// Delete the tasktask.reset();// The thread is wanted to stop, return even// if the queue is not empty yetif (stop){return;}// Get a new taskhas_new_task = this->func_tasks.pop(task);}// At this point the queue has run out of tasks, wait here for more.// Thread is now idle.// If all threads are idle, notify any waiting in wait().if (++this->_n_idle == this->size()){std::lock_guard<std::mutex> lock(this->waiter_mut);this->waiter.notify_all();}std::unique_lock<std::mutex> lock(this->signal_mut);// While the following evaluates to true, wait for a signal.this->signal_cv.wait(lock, [this, &task, &has_new_task, &stop](){// Try to get a new task. This will fail if the thread was// woken up for another reason (stopping or resizing), or// if another thread happened to grab the task before this// one got to it.has_new_task = this->func_tasks.pop(task);// If there is a new task or the thread is being told to stop,// stop waiting.return has_new_task || this->done || stop;});// Thread is no longer idle.--this->_n_idle;// if the queue is empty and it was able to stop waiting, then// that means the thread was told to stop, so stop.if (!has_new_task){return;}}});}

一般emplace_thread在主线程中被调用,通常调用一次就会多一个工作线程,this->threads.emplace_back([this, stop_flag]{}这一行代码完成了一个工作线程的开启,工作的内容即是大括号内的匿名函数。

这个函数内部有两层循环,最内部的循环总是判断是否有新的任务,有的话通过(*task)()执行之前包装的匿名函数,再由那个被包装的匿名函数调用真正需要执行的函数。执行完后取下一个新线程,队列中若没有新线程则跳出内层的while循环,在外层等待新任务的信号。

三、小结

前面两个是线程池最关键的代码段,有了这个基础,再顺着代码调试一下,基本上就清晰了。当然,这里面还有关于C++11的用法,模板相关的同样比较绕人,不过那些小知识点可以通过小的练习完成,不会对理解这个程序造成太大的困扰。

后面再找时间做一个扩展,看如何在这个基础上进行定时器的逻辑推演,其实原先拿到的程序是定时器相关的东西,只不过觉得那个太复杂,于是花了很大的精力将其去除,保留核心。

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

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

相关文章

第二十三章 Git

一、Git Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同&#xff0c;它采用了分布式版…

MIT6.828实验:Xv6 and Unix utilities

2023MIT6.828 lab-1 官方地址 一、sleep 实验内容 调用sleep&#xff08;系统调用&#xff09;编写用户级别程序能暂停特定时常的系统滴答程序保存在user/sleep.c 实验过程 xv6的参数传递 查看官方文档提示的文件中&#xff0c;多采用如下定义&#xff1a; int main(in…

【网站项目】三省学堂-学习辅助系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Redis中的Sentinel(一)

Sentinel 概述 Sentinel(哨岗、哨兵)是Redis的高可用性(high availability)解决方案:由一个或多个Sentinel实例(instance)组成的Sentinel系统(system)可以监视任意多个主服务器&#xff0c;以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时&#xff0…

JAVA毕业设计133—基于Java+Springboot+Vue的网上宠物店商城管理系统(源代码+数据库+12000字论文)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootVue的网上宠物店商城管理系统(源代码数据库12000字论文)133 一、系统介绍 本项目前后端分离&#xff0c;分为管理员、用户两种角色 1、用户&#xff1a; 注册…

python-基础篇-字符串、列表、元祖、字典-字符串

文章目录 2.3字符串、列表、元祖、字典2.3.1字符串2.3.1.1字符串介绍2.3.1.1.1python中字符串的格式&#xff1a;2.3.1.1.2字符串在内存中的存储方式 2.3.1.2字符串的输入输出2.3.1.2.1字符串输出2.3.1.2.2字符串输入2.3.1.2.3组字符串的方式 2.3.1.3下标和切片2.3.1.3.1下标索…

Android 系统大致启动流程

Android启动流程大体为&#xff1a;BootRom -> BootLoader -> Kernel -> Init -> Zygote -> SystemServer ->Launcher 1、Loader层 1.1、Boot ROM 电源按下&#xff0c;引导芯片代码开始从预定义的地方&#xff08;固化在ROM&#xff09;开始执行&#xff0…

Unity入门

Unity入门学习 知识概述&#xff1a; Unity环境搭建 1.Unity引擎是什么 2.软件下载安装 下载最新的长期支持版即可 3.新工程和工程文件夹 Unity界面基础 1.Scene场景和Hierachy层级窗口 练习&#xff1a; 2.Game游戏和Project工程 3.Inspector检查和Console控制台 练习&#…

医疗器械5G智能制造工厂数字孪生可视化平台,推进行业数字化转型

医疗设备5G智能制造工厂数字孪生可视化平台&#xff0c;推进行业数字化转型。在数字化浪潮的推动下&#xff0c;医疗设备行业正迎来一场深刻的变革。5G技术的崛起&#xff0c;智能制造工厂的兴起&#xff0c;以及数字孪生可视化平台的出现&#xff0c;正在共同推动医疗设备行业…

Xshell Plus 详细安装教程以及附带使用图文教程

一、下载 Xshell Plus 6 完成后&#xff0c;请按照下面教程操作 1、下载 Xshell Plus 6 完成后&#xff0c;并解压 zip 包: 2、进入解压后的文件夹后&#xff0c;如果你之前安装了 Xshell&#xff0c; 先点击 !卸载.bat 卸载 xshell&#xff0c; 然后再点击 !绿化.bat; 如果是…

二分答案(砍树,借教室)

二分的两种情况附代码&#xff1a; 二分查找条件&#xff1a;单调&#xff0c;二段性 例题1&#xff1a;P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 上代码&#xff1a; #include<bits/stdc.h> using namespace std; const …

校招说明书

3400字的详细说明&#xff0c;介绍了程序员类岗位校招的整体时间节点和招聘流程。还对一些常见的问题进行讨论&#xff0c;例如内推、offer和三方、实习等。 第一章介绍基本的术语&#xff0c;第二章介绍整个校招的重要流程及时间点&#xff0c;然后第三章介绍每次招聘要经过的…

MySql 实战大数据查询-(表分区实现)

一 mysql分区&#xff1a; 分区是将单个表按照某种规则划分成多个子集&#xff0c;每个子集称为一个分区。常见的分区策略包括按照时间范围、范围值、列表等进行分区。 优点&#xff1a; 查询性能更好&#xff0c;涉及分区键的查询&#xff0c;数据库引擎可以只扫描特定分区&…

易宝OA ExecuteQueryForDataSetBinary SQL注入漏洞复现

0x01 产品简介 易宝OA系统是一种专门为企业和机构的日常办公工作提供服务的综合性软件平台,具有信息管理、 流程管理 、知识管理(档案和业务管理)、协同办公等多种功能。 0x02 漏洞概述 易宝OA ExecuteQueryForDataSetBinary 接口处存在SQL注入漏洞,未经身份认证的攻击者…

Word中插入Endnote参考文献时显示乱码

近期在写文章需要插入参考文献&#xff0c;使用Endnote插入时显示乱码&#xff0c;如下图所示&#xff1a; 文章末尾显示{ADDIN EN REFILIST } 解决方法 在网上找了诸多方法尝试也没有解决&#xff0c;最终找到一篇博客介绍了一种方法&#xff1a; word选项—高级&#xff1…

openGauss学习笔记-256 openGauss性能调优-使用Plan Hint进行调优-优化器GUC参数的Hint

文章目录 openGauss学习笔记-256 openGauss性能调优-使用Plan Hint进行调优-优化器GUC参数的Hint256.1 功能描述256.2 语法格式256.3 参数说明 openGauss学习笔记-256 openGauss性能调优-使用Plan Hint进行调优-优化器GUC参数的Hint 256.1 功能描述 设置本次查询执行内生效的…

LAN和WAN, 调制解调器, 路由器,交换机 区别

LAN LAN&#xff08;Local Area Network&#xff09;是指在相对较小的地理范围内&#xff08;如办公室、学校、实验室、家庭等&#xff09;连接在一起的计算机和网络设备的集合。LAN通常由路由器、交换机、网线、无线路由器等设备组成&#xff0c;用于连接多台计算机、打印机、…

react状态管理库---zustand

一个简单的&#xff0c;快速的状态管理解决方案&#xff0c;api设计基于函数式和hooks 安装&#xff1a; npm install zustand 基础使用 让我们实现一个非常简单的计数器案例完成我们的第一个store 1- 创建一个counterStore create( ) 有三个参数&#xff1a;函数、布尔值…

Leetcode - 127双周赛

目录 一&#xff0c;3095. 或值至少 K 的最短子数组 I 二&#xff0c;3096. 得到更多分数的最少关卡数目 三&#xff0c;3097. 或值至少为 K 的最短子数组 II 四&#xff0c;3098. 求出所有子序列的能量和 一&#xff0c;3095. 或值至少 K 的最短子数组 I 本题需要知道一个知…

先进电气技术 —— (控制理论)何为稳定性?

一、系统稳定性 在控制理论中&#xff0c;系统稳定性是一个非常关键的概念&#xff0c;它主要涉及系统对外界扰动或内部变动的响应行为。以下是与系统稳定性相关的一些核心名词及其解释&#xff1a; 基本概念 稳定性&#xff08;Stability&#xff09; 系统稳定性是指当系统受…