【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实现哈希表) 本…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...