【Linux】线程池实现
📗线程池实现(单例模式)
- 1️⃣线程池概念
- 2️⃣线程池代码样例
- 3️⃣部分问题与细节
- 🔸类成员函数参数列表中隐含的this指针
- 🔸单例模式
- 🔸一个失误导致的bug
- 4️⃣调用线程池完成任务
1️⃣线程池概念
线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
线程池的应用场景:
- 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误.
线程池示例:
- 创建固定数量线程池,循环从任务队列中获取任务对象,
- 获取到任务对象后,执行任务对象中的任务接口
2️⃣线程池代码样例
以下为线程池代码:
#pragma once#include <iostream>
#include <queue>
#include <pthread.h>
#include <ctime>template<class T>
class ThreadPool
{private:std::queue<T> _q;//任务队列pthread_mutex_t _lock;pthread_cond_t _cond;//有任务时提醒线程执行任务static ThreadPool<T>* _instance;ThreadPool(){} public:static ThreadPool<T>* getInstance()//单例模式,饿汉模式,静态成员{static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;if(nullptr == _instance)//双if提高效率,因为只有第一次获取_instance时才会实例化,后续无需再加锁实例化_instance{pthread_mutex_lock(&mtx);if(nullptr == _instance){_instance = new ThreadPool<T>();}pthread_mutex_unlock(&mtx);}return _instance;}void MutexInit(){pthread_mutex_init(&_lock, nullptr);}void MutexLock(){pthread_mutex_lock(&_lock);}void MutexUnLock(){pthread_mutex_unlock(&_lock);}bool IsEmpty(){return _q.size() == 0 ? true : false;}void ThreadWait(){pthread_cond_wait(&_cond, &_lock);}void ThreadWakeUp(){pthread_cond_signal(&_cond);}void PopTask(T* out)//取任务{//此处不应加锁,应为取任务的时候是带着锁的,若此时申请锁,会出现死锁现象*out = _q.front();_q.pop();}//类内部的成员方法都有隐含的this参数,因此要加上static修饰static void* Routine(void* args){ThreadPool<T>* tp = (ThreadPool<T>*) args;//接收this指针pthread_detach(pthread_self());//线程分离while(true){tp->MutexLock();//加锁,访问临界区_qwhile(tp->IsEmpty())//任务队列为空,挂起等待{tp->ThreadWait();}//到此处说明有任务T t;tp->PopTask(&t);tp->MutexUnLock();//退出临界区_qt();}return nullptr;}void ThreadPoolInit(int num)//初始化线程池{pthread_t p[num];for(int i = 0; i < num; i++){pthread_create(p + i, nullptr, Routine, this);//将this指针作为参数传入}}void PushTask(const T& in){//分配任务MutexLock();_q.push(in);MutexUnLock();ThreadWakeUp();//唤醒线程完成任务}~ThreadPool(){pthread_mutex_destroy(&_lock);}
};template<class T>
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;
3️⃣部分问题与细节
下面分享一些在编写该单例模式线程池代码遇到的一些问题与细节:
🔸类成员函数参数列表中隐含的this指针
我们在初始化线程池的这部分代码,需要创建若干线程来完成其所需要执行的任务,这些线程的例程函数形式为void *(*start_routine) (void *) ,其参数列表中仅有一个参数void*,而如果将这个例程函数定义成成员函数,会有一个隐含的this指针参数,导致形式不一致,因此需要将该例程函数用static修饰为静态的。
又因为静态成员函数只能访问静态成员变量,故我们需要在创建线程时将this指针通过参数传递给例程函数,这样才能在例程函数中使用this指针访问类中的成员变量。

🔸单例模式
我们这个线程池设计成了单例模式,并且采用的是饿汉模式,即服务启动后只有在用到线程池这个功能时才会创建对象。而在单例模式创建对象时,由于只有第一次创建对象时对象指针为nullptr,故判断是否要创建对象指针的时候可以在加锁之前再进行一次判断提高效率,而无需每次都要先加锁再判断。

🔸一个失误导致的bug
在线程取任务的接口设计时,我因为这里需要访问任务队列这个临界区给这个过程加上了锁,但是实际上在调用这个接口的时候其实线程就已经申请加了锁,而且两次申请的为同一把锁,就导致出现了线程在已经持有一把锁的情况下又去申请这把锁,从而产生了死锁。

4️⃣调用线程池完成任务
任务类:
实现x 与 y 的+ - * / % 五种运算。
#pragma once
#include <iostream>class Task//x op y = z
{private:int x;int y;char op;//+-*/%public:Task(){}Task(int _x, int _y, char _op):x(_x),y(_y),op(_op){}void operator()(){int z = -1;switch (op){case /* constant-expression */'+':/* code */z = x + y;break;case '-':z = x - y;break;case '*':z = x * y;break;case '/':if(0 != y)z = x / y;elsestd::cout << "warning: div zero error" << std::endl;break;case '%':if(0 != y)z = x % y;elsestd::cout << "warning: div zero error" << std::endl;break;default:std::cout << "unkonwn operator" << std::endl;break;}std::cout << "[" << pthread_self() << "] handler task: " << x << " " << op << " " << y << " = " << z << std::endl;}~Task(){}
};
主函数:
#include "thread_pool.hpp"
#include "Task.hpp"
#include <string>
#include <unistd.h>int main()
{srand((unsigned long)time(nullptr));ThreadPool<Task>* tp = ThreadPool<Task>::getInstance();tp->ThreadPoolInit(5);const std::string s = "+-*/%";while(true){int x = rand() % 50;int y = rand() % 50;char op = s[rand() % 5];Task t(x, y, op);tp->PushTask(t);sleep(1);}return 0;
}

结果如上,左侧为线程池中的线程每隔一秒取出任务并执行,右侧为线程池的情况。
相关文章:
【Linux】线程池实现
📗线程池实现(单例模式) 1️⃣线程池概念2️⃣线程池代码样例3️⃣部分问题与细节🔸类成员函数参数列表中隐含的this指针🔸单例模式🔸一个失误导致的bug 4️⃣调用线程池完成任务 1️⃣线程池概念 线程池是…...
使用Python批量上传本地maven库到nexus
背景:外包类项目开发时是调用的公司maven仓库进行开发,交付后需要将maven仓库转移到客户环境。 原理:1、打开idea运行源代码,将maven包下载到本地仓库, 2、下载包所在目录中执行脚本将本地仓库的maven包上传到客户nex…...
【Unity实战100例】Unity对Ini格式的配置文件管理和读写
目录 一.编写ini格式配置文件 二.读取解析ini文件 三.调用属性 INI 文件以文本形式存储,易于阅读和编辑。这种人可读的格式使得调整配置参数变得更加直观,不需要专门的工具。 INI 文件是一种轻量级的配置文件格式,不需要复杂的解析器或库。它的结构相对简单,适用于小到...
k8s存储卷和数据卷下
静态pv和pvc 运维负责pv:创建号持久化存储卷,申明好读写和挂载类型,以及可以提供的存储空间 Pvc开发做,要和开发沟通好,你期望的读写和挂载类型,以及存储空间 当我发布vc之后可以生成pv,还可以在…...
SQL Server 配置远程连接
Windows 安装好 SQL Server 的 SSMS,打开SSMS配置远程连接 找到 配置管理器 启用 TCP/IP 打开防火墙设置 新建入站规则 端口TCP - 特定本地端口 (1433)允许连接下一步名称完成 重启 SQL Server 服务...
vscode(visual studio code) 免密登陆服务器
1.生成密钥 首先,在本地,打开命令输入框: WinR–>弹出输入框,输入cmd,打开命令框。 然后,在命令框,输入 ssh-keygen -t rsa -C "love"按两次回车键,问你是否重写,选择…...
[redis] redis主从复制,哨兵模式和集群
一、redis的高可用 1.1 redis高可用的概念 在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 高可用的计算公式是1-(宕机时间)/(宕机时…...
debian12部署Gitea服务
首先安装git、wget、sqlite,然后进行用户和组的相关设置 sudo apt install -y git wget sqlite3 新增一个git用户与一个git组 sudo adduser --system --group --disabled-password --shell /bin/bash --home /home/git --gecos Git Version Control git 给git用户设…...
js动态设置关键侦@keyframes
js动态设置关键侦keyframes 1.前置知识 关键侦keyframes规则通过在动画序列中定义关键侦的样式来控制CSS动画序列的中间步骤 keyframes slidein {from {transform: translateX(0%);}to {transform: translateX(100%);} } // from 等价于 0%;to 等价与 100% // 或…...
【WPF.NET开发】流文档
本文内容 什么是流文档?流文档类型创建流内容与流相关的类内容架构自定义文本 流文档旨在优化查看和可读性。 流文档根据运行时变量(例如,窗口大小、设备分辨率和可选的用户首选项)来动态调整和重新排列内容,而不是设…...
golang学习-结构体
1、定义 使用type 和struct 关键字来定义结构体,是值类型 格式如下: type 类型名 struct { 字段名 类型 字段名 类型 ... } 2、实例化 1、var 结构体实例 结构体类型 var p1 Person 2、使用new关键字 var p2 new(Person) 3、使用&对结构体…...
Python:enumerate() 函数
enumerate() 函数用于同时遍历索引和元素,常用于循环中。这个函数返回一个包含索引和元素的元组,可以通过解包的方式获取它们。 使用方法: enumerate(iterable, start0). iterable: 要遍历的可迭代对象。start: 索引起始值,默认…...
FPGA 移位运算与乘法
题目: 已知d为一个8位数,请在每个时钟周期分别输出该数乘1/3/7/8,并输出一个信号通知此时刻输入的d有效(d给出的信号的上升沿表示写入有效) 由题意可知: 复位信号高有效,低复位;在inpu_grant上升…...
网络安全B模块(笔记详解)- MYSQL信息收集
MYSQL信息收集 1.通过渗透机场景Kali中的渗透测试工具对服务器场景MySQL03进行服务信息扫描渗透测试(使用工具Nmap,使用必须要使用的参数),并将该操作显示结果中数据库版本信息作为Flag提交; Flag:MySQL 5.5.12 2.通过渗透机场景Kali中的渗透测试工具对服务器场景MySQL0…...
从JavaScript的角度上讲解一下xml
- XML(可扩展标记语言) XML(可扩展标记语言)是一种被设计用于存储和传输结构化数据的标记语言。它与HTML相似,但XML并没有预定义的标签,可以自定义标签及其属性。从JavaScript的角度来看,XML可以…...
Pandas实战100例 | 案例 13: 数据分类 - 使用 `cut` 对数值进行分箱
案例 13: 数据分类 - 使用 cut 对数值进行分箱 知识点讲解 在数据分析中,将连续的数值数据分类成不同的区间(或“分箱”)是一种常见的做法。Pandas 提供了 cut 函数,它可以根据你指定的分箱边界将数值数据分配到不同的类别中。 …...
python统计分析——操作案例(模拟抽样)
参考资料:用python动手学统计学 import numpy as np import pandas as pd from matplotlib import pyplot as plt import seaborn as snsdata_setpd.read_csv(r"C:\python统计学\3-4-1-fish_length_100000.csv")[length] #此处将文件路径改为自己的路…...
部署Tomcat及其负载均衡
Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。一般来说,Tomcat虽然和Apache或者Nginx这些Web服务器一样…...
C++ 类、结构体
C 类、结构体 类可以将变量、数组和函数完美地打包在一起。 类与结构体 类的定义: class Person {private:int age,height;double money;string books[100];public:string name;void say(){cout<<"Im"<<name<<endl;}int get_age(){…...
数据结构(三)堆和哈希表
目录 哈希表和堆什么是哈希表 ?什么是堆 ?什么是图 ?案例一:使用python实现最小堆案例二 : 如何用Python通过哈希表的方式完成商品库存管理闯关题 (包含案例三:python实现哈希表) 本…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
