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

(线程池) 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) 线程池是一种多线程处理形式&#xff0c;处理过程中将任务添加到队列&#xff0c;然后在创建线程后自动启动这些任务。线程池线程都…...

Mysql按姓氏从小到大排序的正确sql

一、前言 最近有个需求&#xff0c;要按姓氏从小到大查询数据。(姓名都是中文的) 写了一个sql&#xff1a; select a.* from mytable a order by substr(a.NAME,1,1) asc结果发现这样不行&#xff0c;排序是乱的。 二、解决办法 查询发现&#xff0c;如果mysql字符集是gbk的…...

【C++】详细介绍模版初阶—函数模版、类模板

文章目录 一、泛型编程二、函数模版2.1 函数模版概念2.2 函数模版格式2.3 函数模版的原理2.4 函数模版的实例化2.5 函数模版的匹配原则 三、类模版3.1 类模版定义3.2 类模版实例化 总结 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)&#xff89;&…...

BananaPi BPI-6202工业控制板全志科技A40i、24V DC输入、RS485接口

Banana Pi BPI-6202“嵌入式单板计算机”采用工业级全志A40i四核Cortex-A7处理器&#xff0c;工业温度范围和长生命周期&#xff0c;2GB DDR3&#xff0c;8GB eMMC闪存&#xff0c;M.2 SATA插槽等。 这是自 Banana Pi去年推出Banana Pi BPI-M2 Ultra SBC 和BPI-M2 Berry以来&am…...

Python - functools.partial设置回调函数处理异步任务基本使用

一. 前言 在Python中&#xff0c;回调函数是指在一个函数执行完成后&#xff0c;调用另一个函数的过程。通常情况下&#xff0c;回调函数作为参数传递给原始函数&#xff0c;原始函数在执行完自己的逻辑后&#xff0c;会自动调用回调函数并将结果作为参数传递给它。 二. 回调…...

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…...

结算日-洛谷

结算日 - 洛谷 解释&#xff1a; 1.用sum记录贝西走到某位置的累计的总钱&#xff0c;flag标记是否有欠债还不了的情况&#xff08;1为有&#xff09;&#xff0c;ans记录步数。 2.若sum<0&#xff0c;则欠债无法还&#xff0c;flag标记为1&#xff0c;并记录下此刻的位置…...

Android Native Code开发学习(一)环境配置

Android Native Code开发学习&#xff08;一&#xff09; 本教程为native code学习笔记&#xff0c;希望能够帮到有需要的人 我的电脑系统为ubuntu 22.04&#xff0c;当然windows也是可以的&#xff0c;区别不大 环境配置 首先我们新建一个native C项目 然后我们下载NDK和C…...

Python GUI应用程序开发之wxPython使用详解

概要 wxPython是一个强大的跨平台GUI工具包&#xff0c;它使用Python编程语言开发&#xff0c;提供了丰富的控件功能。如果你是一名Python开发者&#xff0c;而且希望创建一个功能齐全的桌面应用程序&#xff0c;那么wxPython是一个值得考虑的选择。 什么是wxPython wxPython…...

【电子学会真题】青少年软件编程(C语言)等级考试试卷(一级) 2021年9月

试卷下载 pdf 格式下载&#xff1a;https://download.csdn.net/download/SHUTIAN2010/88255543 word 格式下载&#xff1a;https://download.csdn.net/download/SHUTIAN2010/88255558 1&#xff0e;计算乘积 一行两个整数a、b&#xff0c;以空格分隔。&#xff08;0&#xff1…...

学习完毕JavaSE的感想

今天&#xff0c;把Java复习完毕了&#xff0c;之前学习的时候&#xff0c;学校里学的总是有限的 &#xff0c;自己上手操作之后才发觉差的很多&#xff0c;部署服务器发现要学操作系统&#xff0c;学完了web基础 &#xff0c;又发现还得学前后端分离vue react这些&#xff0c;…...

FastJson的学习

fastjson是阿里巴巴的开源JSON解析库&#xff0c;它可以解析JSON格式的字符串&#xff0c;支持将Java Bean序列化为JSON字符串&#xff0c;也可以从JSON字符串反序列化到JavaBean。 fastjson是json的序列化和反序列化 一、添加依赖 <dependency><groupId>com.ali…...

python scrapy框架

scrapy概述 Scrapy&#xff0c;Python开发的一个快速、高层次的屏幕抓取和web抓取框架&#xff0c;用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛&#xff0c;可以用于数据挖掘、监测和自动化测试 scrapy安装 pip install scrapy -i https://pypi.tuna.tsinghua…...

滑动窗口系列3-Leetcode134题加油站

在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发&#xff0c;开始时油箱为空。 给定两个整数数组 gas 和 cost &…...

LOIC(low orbit ion cannon)

前言 重要的话说三遍&#xff1a; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上&#xff01;&#xff01;&#xff01; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上&#xff01;&#xff01;&#xff01; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上…...

从格灵深瞳中报稳定盈利,看AI公司的核心竞争力

2023年过半&#xff0c;人工智能产业话题不断。大模型和AIGC掀起热潮&#xff0c;让众多AI公司开始进入新一轮竞赛。但与此同时&#xff0c;不少AI公司依然处于亏损中&#xff0c;研发投入和商业产出难以实现正循环。如何形成健康的商业模式&#xff0c;仍是一大挑战。 AI公司…...

理解 Databend Cluster key 原理及使用

Databend Cluster Key 是指 Databend 可以按声明的 key 排序存储&#xff0c;主要用于用户对时间响应比较高&#xff0c;同时愿意为这个 cluster key 进行额排序操作的用户。 Databend 只支持一个 Cluster key&#xff0c;Cluster key中可以包含多列及表达式。 基本语法 -- 语…...

C++day3(类、this指针、类中的特殊成员函数)

一、Xmind整理&#xff1a; 二、上课笔记整理&#xff1a; 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错误 在激活时&#xff0c;先不打开应用&#xff0c;先用管理员身份打开注册机Navicat_Keygen_Patch_v5.6_By_DFoX.exe&#xff0c;Navicat v15——>MySql——>Simplified Chinese——>Patch&#xff0c;执行完这些步骤之后…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...