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

【Linux 23】线程池

文章目录

  • 🌈 一、线程池的概念
  • 🌈 二、线程池的应用场景
  • 🌈 三、线程池的实现

🌈 一、线程池的概念

  • 线程池 (thread pool) 是一种利用池化技术的线程使用模式

  • 虽然创建线程的代价比创建进程的要小很多,但小并不意味着没有。如果每次处理任务都要创建线程,积少成多下,代价还是蛮高的。

  • 而线程池就可以解决这种问题,提前先创建出一批线程,没任务时,让这些线程泡在池子里睡觉,来任务后,将这些线程从池子里叫起来干活。

    • 水池里流淌的是水流,而线程池里流淌的则是执行流 (线程)。
  • 不要被线程池这个称呼给套住了,本质上来说,线程池 = 任务队列 + 条件变量 + 多执行流

    • 当任务队列中没任务时,让一堆线程在指定条件变量下等待;
    • 当任务队列中有任务时,会唤醒在条件变量下等待的线程,让被唤醒的线程从任务队列中获取任务。
  • 说到底,线程池本质上就是个生产消费模型

image-20240923163036559

🌈 二、线程池的应用场景

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。
    • 例如 web 服务器完成网页请求,这种单个任务小,但任务量大的任务,就很适合使用线程池技术。
  2. 对性能要求苛刻的应用,要求服务器迅速响应客户请求。
  3. 接收突发性的大量请求,但不至于使服务器因此产生大量线程的应用。
    • 在没有线程池的情况下,如果没有线程池,短时间内将产生大量线程,可能使内存到达极限。

🌈 三、线程池的实现

  • 当前要实现一个主线程不停的往任务队列中放任务,然后让线程池中的线程从任务队列中获取任务,再进行任务处理的一个简易线程池。

  • 实现的这个简易的线程池整体分为线程池部分代码、任务类型部分代码、主线程部分代码这三部分。

  • 这三部分代码可以分别写在三个文件中 (记得包含对应头文件),也可以放在一个文件中,此处为了方便演示就全写在一个文件中。

#include <queue>
#include <iostream>
#include <unistd.h>
#include <pthread.h>using std::cerr;
using std::cout;
using std::endl;
using std::queue;/* ---------- 任务类型代码设计 ---------- */
class task
{
private:int _x;   // 左操作数int _y;   // 右操作数char _op; // 操作符public:task(int x = 0, int y = 0, char op = 0): _x(x), _y(y), _op(op){}// 处理任务的方法void run(){int result = 0; // 记录计算结果switch (_op){case '+':result = _x + _y;break;case '-':result = _x - _y;break;case '*':result = _x * _y;break;case '/':if (_y == 0){cerr << "除零错误" << std::endl;return;}else{result = _x / _y;}break;case '%':if (_y == 0){std::cerr << "模零错误" << std::endl;return;}else{result = _x % _y;}break;default:cerr << "非法运算符" << endl;return;}cout << "新线程 [" << pthread_self() << "] 获取任务成功, 任务的处理结果为: "<< _x << " " << _op << " " << _y << " = " << result << endl;}~task(){}
};/* ---------- 线程池代码设计 ---------- */
#define NUM 5               // 任务队列的容量template <typename T>
class thread_pool
{
private:queue<T> _task_queue;   // 任务队列int _thread_num;        // 线程池中线程的数量pthread_mutex_t _mutex; // 线程池中的线程的互斥锁pthread_cond_t _cond;   // 线程池中的线程的条件变量private:// 判断任务队列是否为空bool is_empty(){return _task_queue.size() == 0;}// 上锁void lock_queue(){pthread_mutex_lock(&_mutex);}// 解锁void unlock_queue(){pthread_mutex_unlock(&_mutex);}// 让线程去指定条件变量处等待等待void wait(){pthread_cond_wait(&_cond, &_mutex);}// 唤醒在指定条件变量处等待的线程void wakeup(){pthread_cond_signal(&_cond);}public:// 线程池的构造函数thread_pool(int num = NUM): _thread_num(num){pthread_mutex_init(&_mutex, nullptr); // 初始化互斥锁pthread_cond_init(&_cond, nullptr);   // 初始化条件变量}// 线程池中的线程所要执行的函数static void *routine(void *arg){pthread_detach(pthread_self());thread_pool *self = (thread_pool *)arg;// 不断从任务队列获取中任务,然后处理这些任务while (true){self->lock_queue();     	// 获取任务前, 先对任务队列上锁while (self->is_empty())	// 如果任务队列为空, 则让线程去休眠self->wait();T task;               		// 定义任务对象self->pop(&task);     		// 用定义好的任务对象从任务队列中获取任务self->unlock_queue(); 		// 获取任务后, 要对任务队列解锁task.run();           		// 处理获取到的任务}}// 初始化线程池 (为线程池创建一批线程)void thread_pool_init(){pthread_t tid;for (int i = 0; i < _thread_num; i++)pthread_create(&tid, nullptr, routine, this); // 传入 this 指针作为 routine 函数的参数}// 往任务队列放任务(主线程调用)void push(const T &task){lock_queue();               // 给任务队列上锁_task_queue.push(task);     // 往任务队列中放入任务unlock_queue();             // 为任务队列解锁wakeup();                   // 来活了, 唤醒在线程池中休眠的线程}// 从任务队列获取任务(线程池中的线程调用)void pop(T *task){*task = _task_queue.front();_task_queue.pop();}// 线程池的析构函数~thread_pool(){pthread_mutex_destroy(&_mutex); // 销毁互斥锁pthread_cond_destroy(&_cond);   // 销毁条件变量}
};/* ---------- 主线程代码设计 ---------- */
int main()
{srand((unsigned int)time(nullptr)); // 随机数种子thread_pool<task> *tp = new thread_pool<task>; // 定义线程池对象tp->thread_pool_init();                        // 初始化线程池当中的线程const char *op = "+-*/%";                      // 操作符集// 不断往任务队列塞计算任务while (true)                        {sleep(1);int x = rand() % 100;           // 左操作数int y = rand() % 100;           // 右操作数int index = rand() % 5;         // 随机获取操作符集中的某个操作符的下标task task(x, y, op[index]);     // 构造一个任务对象tp->push(task);                 // 将构造的任务对象放入任务队列cout << "主线程放入任务完毕, 放入的任务为: "<< x << " " << op[index] << " " << y << " = ?" << endl;}return 0;
}

  • 因为没有将屏幕这个临界资源也用锁保护起来,因此才会有一开始的主线程和新线程打印的内容混在一起的状况出现。
  • 要解决这个问题,可以直接使用互斥锁将访问屏幕这个临界资源的临界区保护起来就行。

相关文章:

【Linux 23】线程池

文章目录 &#x1f308; 一、线程池的概念&#x1f308; 二、线程池的应用场景&#x1f308; 三、线程池的实现 &#x1f308; 一、线程池的概念 线程池 (thread pool) 是一种利用池化技术的线程使用模式。 虽然创建线程的代价比创建进程的要小很多&#xff0c;但小并不意味着…...

Rust SQLite 跨平台使用

引言 Rust因其内存安全性和高性能受到越来越多开发者的青睐。在许多项目中&#xff0c;SQLite作为一种轻量级的嵌入式数据库&#xff0c;与Rust的结合为跨平台应用程序提供了强大的支持。本文将详细探讨Rust如何实现跨平台功能&#xff0c;如何在不同平台上使用Rust库&#xf…...

docker运行arm64架构的镜像、不同平台镜像构建

背景 Docker 允许开发者将应用及其依赖打包成一个轻量级、可移植的容器&#xff0c;实现“一次构建&#xff0c;到处运行”的目标。然而&#xff0c;不同的操作系统和硬件架构对容器镜像有不同的要求。例如&#xff0c;Linux 和 Windows 系统有不同的文件系统和系统调用&#…...

vue基于Spring Boot框架的高校实验室预约管理系统

目录 毕设制作流程功能和技术介绍系统实现截图开发核心技术介绍&#xff1a;使用说明开发步骤编译运行代码执行流程核心代码部分展示可行性分析软件测试详细视频演示源码获取 毕设制作流程 &#xff08;1&#xff09;与指导老师确定系统主要功能&#xff1b; &#xff08;2&am…...

Linux中find命令详解

记录linux中find命令的详细用法。 文章目录 find命令简介基本语法常用选项-name-iname-type-size-mtime,-atime,-ctime-perm-user-group-delete-exec-printand or find --help find命令简介 find 是一个搜索目录树以查找一个文件或一组文件的程序。它遍历目录树并报告与用户规…...

无水印短视频素材下载网站有哪些?十个高清无水印视频素材网站分享

你知道怎么下载无水印视频素材吗&#xff1f;今天小编就给大家推荐十个高清无水印视频素材下载的网站&#xff0c;如果你也是苦于下载高清无水印的短视频素材&#xff0c;赶紧来看看吧&#xff5e; 1. 稻虎网 首推的是稻虎网。这个网站简直就是短视频创作者的宝库。无论你需要…...

SpringBoot+Activiti7工作流入门实例

目录 文章目录 目录准备Activiti建模工具1、BPMN-js在线设计器1.1 安装1.2 使用说明1.3运行截图2、IDEA安装Activiti Designer插件2.1安装插件2.2 设置编码格式防止中文乱码2.3 截图简单工作流入门实例1. 新建Spring Boot工程2. 引入Activiti相关依赖添加版本属性指定仓库添加依…...

Azure OpenAI检索增强微调:使用 GPT-4o 对 GPT-4o mini 进行微调,以适应特定领域的应用

定制是关键&#xff01; 生成式人工智能对企业最有影响力的应用之一是创建自然语言界面&#xff0c;这些界面经过定制&#xff0c;可以使用特定领域和用例数据来提供更好、更准确的响应。这意味着回答有关特定领域的问题&#xff0c;例如银行、法律和医疗领域。 我们经常谈…...

ISP Pipeline

系列文章目录 文章目录 系列文章目录前言一、RAW域二、RGB域三、YUV域总结 前言 一、RAW域 黑电平校正&#xff08;BLC&#xff09;数字增益调整&#xff08;DGain&#xff09;自动白平衡&#xff08;AWB&#xff09;局部色调映射&#xff08;LTM&#xff09;坏点修复&#xf…...

< IDE编程环境配置>

IDE编程环境配置 LIB&#xff0c;DLL区别 我们在写项目时会链接&#xff08;调用&#xff09;第3方库&#xff0c;或者比如在vs的解决方案solution创建项目project时&#xff0c;不仅可以开发可执行程序exe&#xff08;可单独运行&#xff09;&#xff08;windows/控制台 应用…...

Golang | Leetcode Golang题解之第448题找到所有数组中消失的数字

题目&#xff1a; 题解&#xff1a; func findDisappearedNumbers(nums []int) (ans []int) {n : len(nums)for _, v : range nums {v (v - 1) % nnums[v] n}for i, v : range nums {if v < n {ans append(ans, i1)}}return }...

【Spring Boot 入门三】Spring Boot与数据库集成 - 构建数据驱动的应用

一、引言 在之前的文章中&#xff0c;我们已经对Spring Boot有了初步的认识&#xff0c;了解了如何构建第一个Spring Boot应用&#xff0c;以及如何通过配置文件来掌控应用的设置。这些知识为我们进一步探索Spring Boot与数据库的集成奠定了坚实的基础。 数据库是现代应用的核…...

Web 服务器与动态脚本语言通信的接口协议有哪些

Web 服务器与动态脚本语言通信的接口协议主要有以下几种&#xff1a; 一、FastCGI&#xff08;Fast Common Gateway Interface&#xff09; 特点&#xff1a;使用持久进程处理请求&#xff0c;减少了进程启动和关闭的开销&#xff0c;提高了性能和可扩展性。多个请求可由同一个…...

ESXI识别服务器磁盘,虚拟机显示无效

ESXI识别服务器磁盘&#xff0c;虚拟机显示无效 系统意外断电识别不到磁盘的情况下可以管理-》硬件-》搜索磁盘名称&#xff0c;选择切换直通&#xff0c;则虚拟机正常。...

【C++】 vector 迭代器失效问题

【C】 vector 迭代器失效问题 一. 迭代器失效问题分析二. 对于vector可能会导致其迭代器失效的操作有&#xff1a;1. 会引起其底层空间改变的操作&#xff0c;都有可能是迭代器失效2. 指定位置元素的删除操作--erase3. Linux下&#xff0c;g编译器对迭代器失效的检测并不是非常…...

【Spring基础3】- Spring的入门程序

目录 3-1 Spring的下载3-2 Spring的 jar 包3-3 第一个 Spring程序第一步&#xff1a;添加spring context的依赖&#xff0c;pom.xml配置如下第二步&#xff1a;添加junit依赖第三步&#xff1a;定义bean&#xff1a;User第四步&#xff1a;编写spring的配置文件&#xff1a;bea…...

golang学习笔记22-面向对象(四):接口【重要】

本节也是GO核心部分&#xff0c;很重要。 注&#xff1a;由于导包语句已经在19讲&#xff08;笔记19&#xff1a;面向对象的引入&#xff09;展示过了&#xff0c;所以这里就不展示了。 一、定义与实现 (1)接口中可以定义一组方法&#xff0c;但不需要实现&#xff0c;不需要…...

SpringBoot技术栈:打造下一代网上租赁系统

第2章 关键技术简介 2.1 Java技术 Java是一种非常常用的编程语言&#xff0c;在全球编程语言排行版上总是前三。在方兴未艾的计算机技术发展历程中&#xff0c;Java的身影无处不在&#xff0c;并且拥有旺盛的生命力。Java的跨平台能力十分强大&#xff0c;只需一次编译&#xf…...

Vue-Lecture1-Notes

渐进式框架 Vue 被称为“渐进式框架”&#xff0c;是因为它允许开发者根据项目的需求逐步引入和使用其功能&#xff0c;而不需要一次性使用整个框架。简单来说&#xff0c;Vue 提供了从简单到复杂的功能层次&#xff0c;可以灵活选择使用。 按需使用&#xff1a;Vue 的核心功能…...

网上租赁系统开发:SpringBoot实践与应用

第2章 关键技术简介 2.1 Java技术 Java是一种非常常用的编程语言&#xff0c;在全球编程语言排行版上总是前三。在方兴未艾的计算机技术发展历程中&#xff0c;Java的身影无处不在&#xff0c;并且拥有旺盛的生命力。Java的跨平台能力十分强大&#xff0c;只需一次编译&#xf…...

17 种 RAG 优化策略

RAG 完整解析 本文适合小白入门&#xff0c;全程用「公司员工手册查病假」为统一实例&#xff0c;清晰讲解 RAG 是什么、工作流程&#xff0c;以及 17 种 RAG 优化策略&#xff08;含标准英文术语&#xff09;&#xff0c;所有内容可直接复制用于分享&#xff0c;实例均精确到具…...

腾讯游戏卡顿终极解决方案:ACE-Guard资源限制器完整指南

腾讯游戏卡顿终极解决方案&#xff1a;ACE-Guard资源限制器完整指南 【免费下载链接】sguard_limit 限制ACE-Guard Client EXE占用系统资源&#xff0c;支持各种腾讯游戏 项目地址: https://gitcode.com/gh_mirrors/sg/sguard_limit 你是否在玩《地下城与勇士》、《英雄…...

语音播报实时

目录 GPT-SoVITS&#xff08;强烈推荐&#xff09; Fish Speech-1.5 GPT-SoVITS&#xff08;强烈推荐&#xff09; RVC-Boss/GPT-SoVITS: 1 min voice data can also be used to train a good TTS model! (few shot voice cloning) Fish Speech-1.5 追求极致流畅的实时对话&a…...

trt 动态batchsize优化:trtexec工具ONNX转engine实战指南

1. 为什么需要动态batchsize优化 在实际的AI模型部署中&#xff0c;我们经常会遇到输入数据量不固定的情况。比如视频分析场景&#xff0c;可能同时有1路或8路视频需要实时处理&#xff1b;又比如在线服务&#xff0c;请求量会随时间波动。这时候如果使用固定batchsize&#xf…...

麒麟V10系统下国产海量数据库安装全攻略(含内核参数优化与避坑指南)

麒麟V10系统下国产海量数据库安装全攻略&#xff08;含内核参数优化与避坑指南&#xff09; 在国产化技术快速发展的今天&#xff0c;越来越多的企业和机构开始采用国产操作系统和数据库产品。麒麟V10作为国产操作系统的代表之一&#xff0c;其稳定性和安全性得到了广泛认可。而…...

当多线雷达遇上RTK:一个能跑工业现场的SLAM方案

多传感器融合建图及定位的工程化落地方案&#xff0c;多线雷达rtk&#xff1b;室内室外导航都适用。 包含部署文档和代码注释&#xff1b;包含工程落地角度的优化。 不含运动控制。 室外场景用RTK信号稳如老狗&#xff0c;一进厂房立马抓瞎&#xff1b;多线雷达在室内横扫千军…...

SWF逆向工程标准化文档:JPEXS Free Flash Decompiler实施指南

SWF逆向工程标准化文档&#xff1a;JPEXS Free Flash Decompiler实施指南 【免费下载链接】jpexs-decompiler JPEXS Free Flash Decompiler 项目地址: https://gitcode.com/gh_mirrors/jp/jpexs-decompiler JPEXS Free Flash Decompiler是一款强大的SWF逆向工程工具&…...

ArduPilot电机控制逻辑与PWM输出机制剖析

1. ArduPilot电机控制基础概念 当你第一次接触无人机飞控时&#xff0c;最让人困惑的莫过于电机控制逻辑了。想象一下&#xff0c;你手里拿着遥控器&#xff0c;轻轻推动摇杆&#xff0c;无人机就能平稳地上升、下降或者转向。这背后到底发生了什么&#xff1f;让我用最直白的…...

STP安全特性实战:如何用bpduguard和bpdufilter防止网络攻击(附真实案例)

STP安全特性实战&#xff1a;如何用bpduguard和bpdufilter防止网络攻击&#xff08;附真实案例&#xff09; 在企业网络架构中&#xff0c;生成树协议&#xff08;STP&#xff09;的安全防护常常被忽视&#xff0c;直到某天凌晨2点&#xff0c;值班工程师突然接到全网瘫痪的告警…...

2026必看:八款热门AI编程工具横评

一、AI编程工具榜单综述当下AI技术全面渗透软件开发领域&#xff0c;各类AI编程工具大幅降低了开发门槛、提升了编码效率&#xff0c;成为开发者必备的效率神器。本次横评精选海内外8款主流产品&#xff0c;覆盖AI原生IDE、插件式编程助手等不同形态&#xff0c;全方位盘点各工…...