Linux——线程池
目录
线程池的概念
线程池的优点
线程池的实现
【注意】
线程池的线程安全
日志文件的实现
线程池的概念
线程池也是一种池化技术,可以预先申请一批线程,当我们后续有任务的时候就可以直接用,这本质上是一种空间换时间的策略。
如果有任务来的时候再创建线程,那成本又要提高,又要初始化,又要创建数据结构。
线程池的优点
- 线程池避免了短时间内创建与销毁线程的代价。
- 线程池不仅能够保证内核充分利用,还能防止过分调度。
线程池的实现
我们这次要实现的线程池就是这样,让主线程派发任务,让线程池中的线程处理任务,这也是一个生产者消费者模型。
// thread.hpp // 把线程封装一下 #pragma once#include <iostream> #include <string> #include <cstdio> #include <vector> #include <queue> #include <unistd.h>using namespace std;typedef void*(*func_t)(void*);class ThreadData { public:string name_;void* args_; };class Thread { public:Thread(int num, func_t callback, void* args):func_(callback){char nameBuffer[64];snprintf(nameBuffer, sizeof(nameBuffer), "Thread-%d", num);name_ = nameBuffer;tdata_.args_ = args;tdata_.name_ = name_;}void start(){pthread_create(&tid_, nullptr, func_, (void*)&tdata_);}void join(){pthread_join(tid_, nullptr);}string name(){return name_;}~Thread(){} private:string name_;pthread_t tid_;ThreadData tdata_;func_t func_; };
// threadPool.hpp#pragma once#include "thread.hpp" #include "lockGuard.hpp" #include "log.hpp"const int g_default_num = 3;template <class T> class ThreadPool { public:// 通过接口获得成员变量pthread_mutex_t* getMutex(){return &lock_;}void waitCond(){pthread_cond_wait(&cond_, &lock_);}bool isEmpty(){return task_queue_.empty();} public:ThreadPool(int thread_num = g_default_num) // 初始化后,就已经有了对象,也有了this指针:num_(thread_num){pthread_mutex_init(&lock_, nullptr);pthread_cond_init(&cond_, nullptr);for (int i = 0; i < num_; i++){threads_.push_back(new Thread(i + 1, routine, this) ); // 通过传入this指针就可以拿到ThreadPool中的task_queue}}void run(){for (auto& iter : threads_){iter->start();cout << iter->name() << "启动成功" << endl;}}// 去掉this指针// 消费的过程static void* routine(void* args){ThreadData* td = (ThreadData*)args;ThreadPool<T>* tq = (ThreadPool<T>*)td->args_; // 去掉this指针就无法访问成员方法了,通过创建线程的时候传入this拿到线程池对象while (true){T task;{lockGuard lockguard(tq->getMutex()); // 加锁while (tq->isEmpty()) tq->waitCond(); // 检测// 读取任务task = tq->getTask();}// 仿函数cout << td->name_ << ", 消费者:" << task._x << " + " << task._y << " = " << task() << endl;// sleep(1);}}void pushTask(const T& task){lockGuard lockguard(&lock_);task_queue_.push(task);pthread_cond_signal(&cond_);}T getTask(){T t = task_queue_.front();task_queue_.pop();return t;}void joins(){for (auto& iter : threads_){iter->join();}}~ThreadPool(){for (auto& iter : threads_){delete iter;}pthread_mutex_destroy(&lock_);pthread_cond_destroy(&cond_);} private:vector<Thread*> threads_;int num_;queue<T> task_queue_; // 任务队列pthread_mutex_t lock_; // 互斥锁pthread_cond_t cond_; // 条件变量 };
// testMain.cc #include "threadPool.hpp" #include "Task.hpp" #include <ctime>int Add(int x, int y) {return x + y; }int main() {srand((unsigned)time(nullptr));cout << "hello thread pool" << endl;ThreadPool<Task> *tp = new ThreadPool<Task>();tp->run();while (true){int x = rand() % 10 + 1;usleep(rand() % 1000);int y = rand() % 10 + 1;Task t(x, y, Add);tp->pushTask(t);cout << "生产者:" << x << " + " << y << " = ? " << endl;//sleep(1);}tp->joins();return 0; }
【注意】
- 线程池中的任务队列会被多个执行流访问,因此我们需要互斥锁对任务队列进行保护。
- 线程池中的线程要从任务队列中拿任务,所以任务队列中必须要先有任务,必须要加锁循环检测,如果任务队列为空,那么该线程应该进行等待,直到任务队列中有任务时再将其唤醒,这些操作都是通过加锁和条件变量完成的。
- 主线程向任务队列中push一个任务后,此时可能有线程正处于等待状态,所以在新增任务后需要唤醒在条件变量下等待的线程。
- 某线程从任务队列中拿到任务后,该任务就已经属于当前线程了,所以解锁之后再进行处理任务,让加锁的动作更细粒度,也因为处理任务的过程会耗费时间,所以不要将处理动作其放到临界区当中。
- 要给执行线程函数用static修饰,这个函数的类型必须是void* (*callback)(void*);如果放到类中,该函数就会多一个this指针。但是让他变成静态函数又不能访问线程池中的任务队列,所以要在线程创建的时候把线程池的对象指针传过去,因为初始化列表后已经有了对象,所以一定有this指针。也因为这个函数没有this指针,所以一些类内的操作要提供接口。
线程池的线程安全
我们也可以把线程池变成单例模式(懒汉模式)的,让整个进程只有一个线程池,但是如果以后有多个线程同时访问,同时判断这个单例对象存不存在,那就会有线程安全的问题。
class ThreadPool { // ... private: // 私有构造,删除拷贝构造和赋值重载ThreadPool(int thread_num = g_default_num) // 初始化后,就已经有了对象,也有了this指针:num_(thread_num){pthread_mutex_init(&lock_, nullptr);pthread_cond_init(&cond_, nullptr);for (int i = 0; i < num_; i++){threads_.push_back(new Thread(i + 1, routine, this) ); // 通过传入this指针就可以拿到ThreadPool中的task_queue}}ThreadPool(const ThreadPool<T>&) = delete;const ThreadPool<T> operator=(const ThreadPool<T>& ) = delete;public:static ThreadPool<T>* getThreadPool(int num = g_default_num) // 通过getThreadPool获取线程池{// 只有第一次为空的时候才创建,如果不为空直接返回thread_ptr,这样指针就只有一个{lockGuard lockguard(&mutex);if (nullptr == thread_ptr){thread_ptr = new ThreadPool<T>(num);}}return thread_ptr;} // ...private:// 添加静态成员变量static ThreadPool<T>* thread_ptr; // 单例模式static pthread_mutex_t mutex; }; // 初始化 template<class T> ThreadPool<T>* ThreadPool<T>::thread_ptr = nullptr;template<class T> pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;
这样就可以保证,第一次获取ThreadPool对象的时候,多个线程访问就是安全的。但这就带来了另一个问题,如果每次想要获取ThreadPool对象的时候就会申请释放锁,这个行为也是在浪费资源,所以还要再调整一下。
static ThreadPool<T>* getThreadPool(int num = g_default_num) {// 只有第一次为空的时候才创建,如果不为空直接返回thread_ptr,这样就只new了一次if (nullptr == thread_ptr) // 多判断一次不就可以了吗,已经创建了就直接返回,没有就加锁创建{lockGuard lockguard(&mutex);if (nullptr == thread_ptr){thread_ptr = new ThreadPool<T>(num);}}return thread_ptr; }
这样使用双重判定空指针就减少了大量已经创建好单例,其他线程还在请求锁的行为。
日志文件的实现
我们需要用到下面这些接口。
// log.hpp #pragma once#include <iostream> #include <string> #include <cstdio> #include <cstdarg> #include <unistd.h> #include <sys/types.h> #include <fcntl.h> #include <ctime>// 日志级别 #define DEBUG 0 #define NORMAL 1 #define WARNING 2 #define ERROR 3 #define FATAL 4const char* gLevelMap[] = {"DEBUG","NORMAL","WARNING","ERROR","FATAL" };// 完整的日志功能,至少有:日志等级 时间 日志内容 支持用户自定义 void logMessage(int level, const char* format, ...) // 最后一个参数就是可变参数列表 {char stdBuffer[1024]; // 日志的标准部分time_t timestamp = time(nullptr); // 时间戳snprintf(stdBuffer, sizeof(stdBuffer), "[%s][%ld]", gLevelMap[level], timestamp);char logBuffer[1024]; // 自定义部分va_list args; // 可变参数列表va_start(args, format);vsnprintf(logBuffer, sizeof (logBuffer), format, args); // 用起来和printf相差不多va_end(args);// printf("%s%s\n", stdBuffer, logBuffer); // 打印到显示器FILE* fp = fopen("log.txt", "a");fprintf(fp, "%s%s\n", stdBuffer, logBuffer); // 打印到文件fclose(fp); }
所以以后如果要用到这些线程池、日志文件等,就直接用了。
相关文章:

Linux——线程池
目录 线程池的概念 线程池的优点 线程池的实现 【注意】 线程池的线程安全 日志文件的实现 线程池的概念 线程池也是一种池化技术,可以预先申请一批线程,当我们后续有任务的时候就可以直接用,这本质上是一种空间换时间的策略。 如果有任…...

Linux:搭建ntp服务器
我准备两个centos7服务器 一个为主服务器连接着外网,并且搭建了ntp服务给其他主机同步 另外一个没有连接外网,通过第一台设备去同步时间 首先两个服务器都要安装ntp软件 yum -y install ntp 再把他俩的时间都改成别的 左侧的是主服务器,主…...

unity学习(57)——选择角色界面--删除角色2
1.客户端添加点击按钮所触发的事件,在selectMenu界面中增加myDelete函数,当点击“删除角色”按钮时触发该函数的内容。 public void myDelete() {string message nowPlayer.id;//string m Coding<StringDTO>.encode(message);NetWorkScript.get…...

Flutter:构建美观应用的跨平台方案
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

【深度学习模型移植】用torch普通算子组合替代torch.einsum方法
首先不得不佩服大模型的强大之处,在算法移植过程中遇到einsum算子在ONNX中不支持,因此需要使用普通算子替代。参考TensorRT - 使用torch普通算子组合替代torch.einsum爱因斯坦求和约定算子的一般性方法。可以写出简单的替换方法,但是该方法会…...

鸿蒙 Harmony 初体验
前言 看现在网上传得沸沸扬扬的鸿蒙,打算弄个 hello world 玩一下, 不然就跟不上时代的发展了 环境安装 我的环境 Windows 11 家庭中文版HarmonyOS SDK (API 9)DevEco Studio (3.1.1 Release)Node.js (16.19.1) 开发IDE下载 官方下载链接 配置 nodejs 这里帮…...

Jmeter+ant,ant安装与配置
1.ant含义 ant:Ant翻译过来是蚂蚁的意思,在我们做接口测试的时候,是可以用来做JMeter接口测试生成测试报告的工具 2.ant下载 下载地址:Apache Ant - Ant Manual Distributions download中选择ant 下载安装最新版zip文件 3.…...

【MySQL基础】MySQL基础操作三
文章目录 🍉1.联合查询🥝笛卡尔积 🍉2.内连接🥝查询单个数据🥝查询多个数据 🍉3.外连接🍉4.自连接🍉5.合并查询 🍉1.联合查询 🥝笛卡尔积 实际开发中往往数…...
【K8s】肿么办??Kubernetes Secrets并不是Secret哟!!
【K8s】肿么办??Kubernetes Secrets并不是Secret哟!! 目录 【K8s】肿么办??Kubernetes Secrets并不是Secret哟!!Kubernetes Secrets为什么不认为 Base64 编码是密文?问题出现了以下是几种加密 K8s Secrets 的选项。Bitnami Sealed Secrets 介绍Bitnami Sealed Secrets…...

数星星 刷题笔记 (树状数组)
依题意 要求每个点 x, y 的左下方有多少个星星 又因为 是按照y从小到大 给出的 所以 我们在计算个数的时候是按照y一层层变大来遍历的 因此我们在处理每一个点的时候 只需要看一下 当前的点有多少个点的x值比当前点小即可 树状数组的 操作模板 P3374 【模板】树…...

Windows→Linux,本地同步到服务器
适用背景: 用自己电脑修改代码,使用实验室/公司的服务器炼丹的朋友 优势: 本地 <--> 服务器,实时同步,省去文件传输的步骤 本地改 -> 自动同步到服务器 -> 服务器跑代码 -> 一键同步回本地ÿ…...

Pycharm连接远程服务器Anoconda中的虚拟环境
在配置远程解释器时,踩过一些坑,现在记录一下配置过程: 步骤1: 打开pycharm的File里面的Settings 里面的Project:你的项目名称目录下的Python Interpreter。 步骤二: 点击右上角的“add interpreter”,选择…...

无人机自动返航算法实现与优化
一、引言 随着无人机技术的快速发展,其在航拍、农业、救援等领域的应用越来越广泛。在这些应用中,无人机的自动返航功能显得尤为重要。一旦无人机失去控制或与遥控器失去连接,自动返航算法能够确保无人机安全返回起飞点,避免损失和…...
切面条-蓝桥杯?-Lua 中文代码解题第1题
切面条-蓝桥杯?-Lua 中文代码解题第1题 一根高筋拉面,中间切一刀,可以得到2根面条。 如果先对折1次,中间切一刀,可以得到3根面条。 如果连续对折2次,中间切一刀,可以得到5根面条。 那么…...

WebRTC:真正了解 RTP 和 RTCP
介绍 近年来,通过互联网进行实时通信变得越来越流行,而 WebRTC 已成为通过网络实现实时通信的领先技术之一。WebRTC 使用多种协议,包括实时传输协议 (RTP) 和实时控制协议 (RTCP)。 RTP负责通过网络传输音频和视频数据,而RTCP负责…...
vue实现双向绑定原理深度解析
1. vue双向绑定应用场景 Vue的双向绑定机制主要体现在以下几个方面: 表单输入:在表单输入中,Vue的双向绑定机制非常有用。通过v-model指令,可以将表单元素的值与Vue实例中的数据进行双向绑定,当用户在表单输入框中输入内容时,数据会自动更新,反之,当数据发生变化时,输…...

C语言 —— memeove函数的模拟实现
函数作用:从sourse的位置开始向后复制num个字节到desttintion指向的内存位置,(运用于字符时,遇到\0不会停止),这个函数支持重叠部分的复制,是memcpy函数的改进版本 具体步骤: 1&am…...

<el-tab>样式自定义——一个可以触类旁通的小例子
首先在网页的检查确定想要自定义的部分叫什么 例如: 我想修改的组件是el-tabs__header.is-top 的margin-bottom 则在相应vue文件的<style>里面增加这一属性 其中,::v-deep可以帮助覆盖子组件内部元素的样式。 ::v-deep .el-tabs__header.is-to…...

XDP学习笔记
XDP的使用与eBPF程序分不开,因此要了解学历XDP,须知道什么是eBPF、什么是XDP。 概念 eBPF BPF(Berkeley Packet Filter)是一种灵活且高效的数据包过滤技术,最初由 BSD Unix 中的网络子系统引入;BPF 允许用…...

JavaScript进阶:js的一些学习笔记-4
文章目录 1. 拷贝1. 浅拷贝2. 深拷贝 2. 异常处理 1. 拷贝 这里指的拷贝是指拷贝引用类型的数据(对象) 1. 浅拷贝 拷贝对象:Object.assign() 或者 {…obj} 展开运算符 const obj {name:liuze,age:23 } const o {...obj}; o.age 22; console.log(o); console.…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...