(线程池) 100行以内的简单线程池
文章目录
- 前言
- Code
- ThreadPool.hpp
- main.cpp
- 简单讲解
- 所需头文件
- using
- 成员变量
- 构造
- 析构
- 添加任务
- PS
- 测试效果
- END
前言
线程池_百度百科 (baidu.com)
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
线程池主要有fixed模式和cached模式。
其中fixed模式实现起来比较简单。本文就是以此来编写。
多线程基础请看:(C++) 多线程之生产者消费者问题_c++ 多线程 生产者消费者_天赐细莲的博客-CSDN博客
cached模式的线程池,可以参考程序喵达人的代码:C++线程池的实现 - 掘金 (juejin.cn)
Code
ThreadPool.hpp
#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
#include <sstream>
#include <string>
#include <thread>
#include <vector>namespace lotus {
std::string get_threadID() {std::stringstream ss;ss << std::this_thread::get_id();return ss.str();
}class ThreadPool {
public:// 统一将任务bind为 void(*)() 的函数形式using Task_Type = std::function<void()>;private: // config// 后期根据这个将 fixed 模式改为 cached模式const size_t TASK_COUNT;private: // taskstd::vector<std::unique_ptr<std::thread>> m_threadControlList;std::queue<Task_Type> m_taskQueue;private: // thread helperstd::atomic<bool> m_canRun;std::condition_variable m_condVar;std::mutex m_mutex;public:/*** @brief Construct a new Thread Pool object* 尽量不要让线程数 > cpu内核数* @param taskCnt*/ThreadPool(const size_t taskCnt) : TASK_COUNT(taskCnt) {m_canRun = (TASK_COUNT <= std::thread::hardware_concurrency());open_pool();}// copy prohibitedThreadPool(const ThreadPool&) = delete;ThreadPool& operator=(const ThreadPool&) = delete;/*** @brief Destroy the Thread Pool object*/~ThreadPool() {close_pool();}public:/*** @brief* 添加任务,并让条件变量通知一次* 目前不处理返回值* @tparam Fun* @tparam Args* @param fun* @param args*/template <typename Fun, typename... Args>void Add_task(Fun&& fun, Args&&... args) {std::lock_guard<std::mutex> lock(m_mutex);auto task =std::bind(std::forward<Fun>(fun), std::forward<Args>(args)...);m_taskQueue.push(std::move(task));m_condVar.notify_one();}private:/*** @brief Create a thread object*/void create_thread() {std::lock_guard<std::mutex> lock(m_mutex);auto createTaskThread = [this]() {for (;;) {std::unique_lock<std::mutex> lock(m_mutex);while (m_taskQueue.empty() && m_canRun) {m_condVar.wait(lock);}if (false == m_canRun) {break;}auto task = std::move(m_taskQueue.front());m_taskQueue.pop();lock.unlock();task();} // while 1};auto thPtr = std::make_unique<std::thread>(createTaskThread);m_threadControlList.emplace_back(std::move(thPtr));}private:/*** @brief* 创建一定数量的线程* @param taskCnt*/void open_pool() {for (size_t i = 0; i < TASK_COUNT; i += 1) {create_thread();}}/*** @brief* 运行标志改为 false* 并通知所有线程* 确保所有线程都join完*/void close_pool() {m_canRun = false;m_condVar.notify_all();for (auto&& thPtr : m_threadControlList) {if (thPtr->joinable()) {thPtr->join();}}}
}; // class} // namespace lotu
main.cpp
#include <iostream>#include "ThreadPool.hpp"
namespace my = lotus;#ifdef _MSC_VER
#define __FUNC_NAME__ __FUNCSIG__
#elif defined(__GNUC__) || defined(__clang__)
#define __FUNC_NAME__ __PRETTY_FUNCTION__
#else
#define __FUNC_NAME__ __func__
#endif/*** @brief* test fun* @param waitTime* @param str*/
void fun(int waitTime, const char* str) {const int N = 5;printf("[%s]time{%d}start\n", __FUNC_NAME__, waitTime);for (int i = 0; i < N; i += 1) {std::this_thread::sleep_for(std::chrono::seconds(waitTime));auto threadId = my::get_threadID();const char* idStr = threadId.c_str();printf("[%s]%s\n", idStr, str);}printf("[%s]time{%d}end\n", __FUNC_NAME__, waitTime);
}/*** @brief* main fun* @param argc* @param argv* @return int*/
int main(int argc, const char** argv) {srand(time(0));printf("[%s] start\n", __FUNC_NAME__);{my::ThreadPool pool(2);pool.Add_task(fun, 1, "11111111111111111111");pool.Add_task(fun, 2, "22222222222222222222");pool.Add_task(fun, 3, "33333333333333333333");system("pause");}printf("[%s] end\n", __FUNC_NAME__);// getchar();
}
简单讲解
所需头文件
#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
using
// 将所有任务bind成void(*)()的形式
using Task_Type = std::function<void()>;
成员变量
private: // config// 后期根据这个将 fixed 模式改为 cached模式const size_t TASK_COUNT;private: // taskstd::vector<std::unique_ptr<std::thread>> m_threadControlList;// 任务队列std::queue<Task_Type> m_taskQueue;private: // thread helper// 终止标志std::atomic<bool> m_canRun;// 条件变量std::condition_variable m_condVar;// 互斥量std::mutex m_mutex;
构造
传入fixed的线程数量, 尽量不要 > cpu核数。禁止拷贝操作。
public:// 传入fixed的线程数量ThreadPool(const size_t taskCnt) : TASK_COUNT(taskCnt) {// 尽量不要 > cpu核数m_canRun = (TASK_COUNT <= std::thread::hardware_concurrency());open_pool();}// 禁止拷贝ThreadPool(const ThreadPool&) = delete;ThreadPool& operator=(const ThreadPool&) = delete;
初始化TASK_COUNT数量的线程。
每个线程写死循环,不断等待和执行任务队列的任务。
基于条件变量std::condition_variable来进行阻塞。
private: void create_thread() {std::lock_guard<std::mutex> lock(m_mutex);auto createTaskThread = [this]() {for (;;) {std::unique_lock<std::mutex> lock(m_mutex);while (m_taskQueue.empty() && m_canRun) {m_condVar.wait(lock);}if (false == m_canRun) {break;}auto task = std::move(m_taskQueue.front());m_taskQueue.pop();lock.unlock();task();} // while 1};auto thPtr = std::make_unique<std::thread>(createTaskThread);m_threadControlList.emplace_back(std::move(thPtr));}private:void open_pool() {for (size_t i = 0; i < TASK_COUNT; i += 1) {create_thread();}}
析构
public:~ThreadPool() {close_pool();}
将运行标志改为false,由于这里的标志是std::atomic<>。并且其余变量不涉及资源竞争,因此无需加锁。
注意,这里使用join()来保证每个线程是正常退出的。
private:void close_pool() {m_canRun = false;m_condVar.notify_all();for (auto&& thPtr : m_threadControlList) {if (thPtr->joinable()) {thPtr->join();}}}
添加任务
这里使用变参模板技术,将任务包装成std::function<void()>的一个可调用对象。
注意这里传入的是万能引用要使用std::forward<>。
每加入一个任务,就让条件变量进行一次通知notify_one()。
public:template <typename Fun, typename... Args>void Add_task(Fun&& fun, Args&&... args) {std::lock_guard<std::mutex> lock(m_mutex);auto task =std::bind(std::forward<Fun>(fun), std::forward<Args>(args)...);m_taskQueue.push(std::move(task));m_condVar.notify_one();}
PS
由于std::this_thread::get_id()返回的是thread::id。对于cpp可以使用std::cout来进行输出。但是并没有对应的类型转化操作。且,id的大小和数值类型在不同平台,不同编译器中不一致。更不能用于printf虽然有的编译器支持。
因此用std::stringstream流进行一次间接的转化。
std::string get_threadID() {std::stringstream ss;ss << std::this_thread::get_id();return ss.str();
}
测试效果
g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0
[int main(int, const char**)] start
[void fun(int, const char*)]time{1}start
[void fun(int, const char*)]time{2}start
请按任意键继续. . . [2]11111111111111111111
[3]22222222222222222222
[2]11111111111111111111
[2]11111111111111111111
[3]22222222222222222222
[2]11111111111111111111
[2]11111111111111111111
[void fun(int, const char*)]time{1}end
[void fun(int, const char*)]time{3}start
[3]22222222222222222222
[3]22222222222222222222
[2]33333333333333333333
[3]22222222222222222222
[void fun(int, const char*)]time{2}end
[2]33333333333333333333
[2]33333333333333333333
[2]33333333333333333333
[2]33333333333333333333
[void fun(int, const char*)]time{3}end[int main(int, const char**)] end
最后的空行是,对应system("pause")。由于在线程池的析构中使用了join(),因此提前键入也可以。
END
相关文章:
(线程池) 100行以内的简单线程池
文章目录 前言CodeThreadPool.hppmain.cpp 简单讲解所需头文件using成员变量构造析构添加任务PS测试效果 END 前言 线程池_百度百科 (baidu.com) 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都…...
Mysql按姓氏从小到大排序的正确sql
一、前言 最近有个需求,要按姓氏从小到大查询数据。(姓名都是中文的) 写了一个sql: select a.* from mytable a order by substr(a.NAME,1,1) asc结果发现这样不行,排序是乱的。 二、解决办法 查询发现,如果mysql字符集是gbk的…...
【C++】详细介绍模版初阶—函数模版、类模板
文章目录 一、泛型编程二、函数模版2.1 函数模版概念2.2 函数模版格式2.3 函数模版的原理2.4 函数模版的实例化2.5 函数模版的匹配原则 三、类模版3.1 类模版定义3.2 类模版实例化 总结 ヾ(๑╹◡╹)ノ" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)ノ&…...
BananaPi BPI-6202工业控制板全志科技A40i、24V DC输入、RS485接口
Banana Pi BPI-6202“嵌入式单板计算机”采用工业级全志A40i四核Cortex-A7处理器,工业温度范围和长生命周期,2GB DDR3,8GB eMMC闪存,M.2 SATA插槽等。 这是自 Banana Pi去年推出Banana Pi BPI-M2 Ultra SBC 和BPI-M2 Berry以来&am…...
Python - functools.partial设置回调函数处理异步任务基本使用
一. 前言 在Python中,回调函数是指在一个函数执行完成后,调用另一个函数的过程。通常情况下,回调函数作为参数传递给原始函数,原始函数在执行完自己的逻辑后,会自动调用回调函数并将结果作为参数传递给它。 二. 回调…...
phpspreadsheet导出excel自动获得列,数字下标
安装composer require phpoffice/phpspreadsheetuse PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Border;$spreadsheet new Spreadsheet(); $sheet $spreadsheet->getActiveSheet();//从65开&a…...
结算日-洛谷
结算日 - 洛谷 解释: 1.用sum记录贝西走到某位置的累计的总钱,flag标记是否有欠债还不了的情况(1为有),ans记录步数。 2.若sum<0,则欠债无法还,flag标记为1,并记录下此刻的位置…...
Android Native Code开发学习(一)环境配置
Android Native Code开发学习(一) 本教程为native code学习笔记,希望能够帮到有需要的人 我的电脑系统为ubuntu 22.04,当然windows也是可以的,区别不大 环境配置 首先我们新建一个native C项目 然后我们下载NDK和C…...
Python GUI应用程序开发之wxPython使用详解
概要 wxPython是一个强大的跨平台GUI工具包,它使用Python编程语言开发,提供了丰富的控件功能。如果你是一名Python开发者,而且希望创建一个功能齐全的桌面应用程序,那么wxPython是一个值得考虑的选择。 什么是wxPython wxPython…...
【电子学会真题】青少年软件编程(C语言)等级考试试卷(一级) 2021年9月
试卷下载 pdf 格式下载:https://download.csdn.net/download/SHUTIAN2010/88255543 word 格式下载:https://download.csdn.net/download/SHUTIAN2010/88255558 1.计算乘积 一行两个整数a、b,以空格分隔。(0࿱…...
学习完毕JavaSE的感想
今天,把Java复习完毕了,之前学习的时候,学校里学的总是有限的 ,自己上手操作之后才发觉差的很多,部署服务器发现要学操作系统,学完了web基础 ,又发现还得学前后端分离vue react这些,…...
FastJson的学习
fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。 fastjson是json的序列化和反序列化 一、添加依赖 <dependency><groupId>com.ali…...
python scrapy框架
scrapy概述 Scrapy,Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试 scrapy安装 pip install scrapy -i https://pypi.tuna.tsinghua…...
滑动窗口系列3-Leetcode134题加油站
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和 cost &…...
LOIC(low orbit ion cannon)
前言 重要的话说三遍: 该程序仅用于学习用途,请勿用于非法行为上!!! 该程序仅用于学习用途,请勿用于非法行为上!!! 该程序仅用于学习用途,请勿用于非法行为上…...
从格灵深瞳中报稳定盈利,看AI公司的核心竞争力
2023年过半,人工智能产业话题不断。大模型和AIGC掀起热潮,让众多AI公司开始进入新一轮竞赛。但与此同时,不少AI公司依然处于亏损中,研发投入和商业产出难以实现正循环。如何形成健康的商业模式,仍是一大挑战。 AI公司…...
理解 Databend Cluster key 原理及使用
Databend Cluster Key 是指 Databend 可以按声明的 key 排序存储,主要用于用户对时间响应比较高,同时愿意为这个 cluster key 进行额排序操作的用户。 Databend 只支持一个 Cluster key,Cluster key中可以包含多列及表达式。 基本语法 -- 语…...
C++day3(类、this指针、类中的特殊成员函数)
一、Xmind整理: 二、上课笔记整理: 1.类的应用实例 #include <iostream> using namespace std;class Person { private:string name; public:int age;int high;void set_name(string n); //在类内声明函数void show(){cout << "na…...
Qt中的配置文件:实现个性化应用程序配置与保存加载
一、前言 在现代软件开发中,用户对于应用程序的个性化配置和设置变得越来越重要。为了满足用户需求并提供更好的用户体验,开发人员常常需要实现一种机制,以便在每次启动应用程序时能够记住用户上次的配置。这样用户就可以方便地恢复到他们熟悉的环境,无需重新进行所有设置…...
Navicat激活时出现rsa public key not find错误
Navicat激活时出现rsa public key not find错误 在激活时,先不打开应用,先用管理员身份打开注册机Navicat_Keygen_Patch_v5.6_By_DFoX.exe,Navicat v15——>MySql——>Simplified Chinese——>Patch,执行完这些步骤之后…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
