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

linux网络编程11——线程池

1. 线程池

1.1 池化技术原理

池化技术

当一个资源或对象的创建或者销毁的开销较大时,可以使用池化技术来保持一定数量的创建好的对象以供随时取用,于是就有了池式结构。常见的池式结构包括线程池、内存池和连接池。

池化技术应用的前提条件主要包括三个,总结一下,以供记忆:

  1. 资源是可复用的。在计算机中,大部分资源都是可复用的,例如内存、线程、进程、tcp连接等。
  2. 资源可以长时间保存。像分配的内存块、已创建的线程、创建的TCP连接,都是可以长时间存在。
  3. 资源创建和销毁的开销较大。如动态内存分配,涉及系统调用和上下文切换,开销较大。

1.2 线程池原理

线程池

线程池是一种维持管理固定数量线程的池式结构。本文讲解了线程池的结构设计和实现。

下图是一个线程池的典型机构:

在这里插入图片描述

其中主要包括三个部件:

  • 生产者线程,是线程池的使用者,它通过向任务队列传递任务交给消费者线程执行。
  • 任务队列。是一个先进先出的队列,由于插入和删除元素都只需要在一段进行读写,因此临界区很短,比较高效。
  • 消费者线程,负责从任务队列中取出任务并执行。

下图是一个消费者线程的状态转换图。从图中可以看到,当任务队列不为空时,消费者线程就不断从中取出任务并执行,否则就会陷入等待态。

在这里插入图片描述

线程池的作用

  1. 性能优化,异步执行耗时任务,不过度占用核心线程,能够提高响应速度和性能。耗时任务可来自于:IO密集型任务(如网络IO和磁盘IO)、CPU密集型任务。
  2. 并发执行,充分利用多核,并发执行核心业务。
  3. 复用线程资源,减少创建和销毁线程的开销。

线程数量如何确定

线程数量取决于任务类型cpu核心数量

  • cpu密集型:cpu核心数目
  • io密集型: cpu核心数目 * (线程等待时间+cpu运算时间) / cpu运算时间

1.3 线程池的应用场景

1.3.1 nginx中的线程池

nginx中的线程池被用于处理文件IO。nginx主要有两个功能,分别是反向代理和提供静态文件资源。

在文件IO中,需要通过内核调用发送数据,涉及到内核态和用户态之间的上下文切换以及数据向内核缓冲区的传输,如果这些操作放在主线程执行将会导致一定的等待时间,减低了响应速度。

因此nginx使用线程池来分离耗时的操作,尤其时文件IO,避免其对主线程的干扰,从而提升 Nginx 的整体性能和响应效率。

1.3.2 redis中的线程池

Redis 默认情况下是单线程的,即它仅开启一个主线程来处理所有的请求。这也是 Redis 一直以来的设计理念,即使用单线程模型实现高性能。

不过,Redis 在 6.0 版本中引入了多线程支持,用于处理客户端的网络读写(如数据的读取、协议解析等),这可以提升在高并发环境下的网络 I/O 性能。

redis的线程池主要用于IO读写和协议解析,不负责执行操作。

1.4 线程池的实现

下面是一个使用C++11写的使用阻塞队列实现的线程池。

先看代码,然后进行讲解。

#include <stdio.h>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
#include <queue>
#include <vector>typedef std::function<void(void)> Task;class ThreadPool
{
public:ThreadPool(size_t thread_count, size_t max_queue_size) :stop_(false), max_queue_size_(max_queue_size){for (size_t i = 0; i < thread_count; ++i){workers_.emplace_back(std::bind(&ThreadPool::worker_run, this));}}void enqueue(Task task){std::unique_lock<std::mutex> lock(mutex_);cond2_.wait(lock, [this] { return stop_ || queue_.size() < max_queue_size_; });if (stop_) return;queue_.emplace(std::move(task));cond1_.notify_one();}~ThreadPool(){{std::lock_guard<std::mutex> lock(mutex_);stop_ = true;}cond1_.notify_all();cond2_.notify_all();for (auto &thread : workers_){thread.join();}}private:void worker_run(){while (true){Task task;{// 出队操作std::unique_lock<std::mutex> lock(mutex_);cond1_.wait(lock, [this] { return stop_ || !queue_.empty(); });if (stop_) return;task = std::move(queue_.front());queue_.pop();cond2_.notify_one();}task();}}private:std::vector<std::thread> workers_;  // 消费者线程std::mutex mutex_;std::condition_variable cond1_;std::condition_variable cond2_;bool stop_;                 // 是否停止运行size_t max_queue_size_;     // 任务队列最大长度std::queue<Task> queue_;    // 任务队列
};

内部主要组件

ThreadPool类中,在功能方面主要包括一个vector<thread>和一个queue<Task>,前者用来保存消费者线程,后者用来保存线程的任务。

另外,需要保证线程安全,因此需要一个mutex和两个条件变量,两个条件变量分别对应的消费者线程的等待队列和生产者线程的等待队列。

最后,stop_成员变量用来通知消费者线程结束执行,max_queue_size_成员变量用来指出queue_的最大尺寸。

对外接口enqueue

对外主要提供一个入队操作enqueue,在本实现中,设置了queue队列最大尺寸,因此该操作可能阻塞线程。

消费者线程主函数worker_run_

消费者线程的主要工作就是不断从任务队列中取出任务并执行。但任务队列为空时,需要消费者线程阻塞。

线程安全保证

通过std::mutex和std::unique_lock,以及std::condition_variable,可以保证线程安全。具体是这样做的:

  1. 生产者线程提交任务时,先持有锁,然后检查任务队列是否已满,如果已满则陷入阻塞,否则将任务插入队列。然后通知消费者线程的等待队列。
  2. 消费者线程获取任务时,先持有锁,然后检查任务队列是否为空,如果为空则陷入阻塞,否则获取一个任务,然后通知生产者线程的等待队列。

线程池的关闭

线程池的关闭,需要先保证每个线程都被关闭,可以通过一个共享变量值的变化通知消费者线程和生产者线程,即设置一个stop变量。

当需要关闭线程池时,设置stop为true,然后当生产者消费者线程看到stop为true时就不再阻塞,并且消费者线程会退出执行,而生产者线程为退出入队函数。

现在的问题在于,我们要保证stop的线程安全以及当stop的值发生变化时,其它线程能够即使看到。对于第一点,我们可以使用std::mutex和std::lock_guard来保证。对于第二点,我们可以让生产者和消费者线程在进入阻塞之前和退出阻塞之后都检查stop的值,如果stop值为true则直接return。

最后在关闭线程时,需要先更改stop的值为true,然后唤醒所有的等待中的线程。

1.5 总结

那么这个线程池就讲到这里。坦白说,这是一个非常基础的线程池,没有尝试使用无锁编程。线程池关闭可能被无限延迟(关闭一般是在程序结束运行时,所以一般问题不大)。另外对于任务类型,设计的也比较简单,没有考虑future和promise等,这个可以留给用户提供的task去自定义。

学习参考

学习更多相关知识请参考零声 github。

相关文章:

linux网络编程11——线程池

1. 线程池 1.1 池化技术原理 池化技术 当一个资源或对象的创建或者销毁的开销较大时&#xff0c;可以使用池化技术来保持一定数量的创建好的对象以供随时取用&#xff0c;于是就有了池式结构。常见的池式结构包括线程池、内存池和连接池。 池化技术应用的前提条件主要包括三…...

MySQL - 主从同步

​​​​​​1.主从同步原理&#xff1a; MySQL 主从同步是一种数据库复制技术&#xff0c;它通过将主服务器上的数据更改复制到一个或多个从服务器&#xff0c;实现数据的自动同步。 主从同步的核心原理是将主服务器上的二进制日志复制到从服务器&#xff0c;并在从服务器上执…...

基于微信小程序的安心陪诊管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…...

深入剖析iOS网络优化策略,提升App性能

一、引言 在当今移动互联网时代&#xff0c;iOS 应用的网络性能直接关系到用户体验。无论是加载速度缓慢、频繁的网络错误&#xff0c;还是高额的流量消耗&#xff0c;都可能导致用户流失。因此&#xff0c;iOS 网络优化成为开发者提升应用质量、增强用户满意度的关键环节。本文…...

游戏开发中常用的设计模式

目录 前言一、工厂模式二、单例模式三、观察者模式观察者模式的优势 四、状态模式状态模式的优势 五、策略模式策略模式的优势 六、组合模式七、命令模式八、装饰器模式 前言 本文介绍了游戏开发中常用的设计模式&#xff0c;如工厂模式用于创建对象&#xff0c;单例模式确保全…...

【PyCharm】远程连接Linux服务器

【PyCharm】相关链接 【PyCharm】连接Jupyter Notebook【PyCharm】快捷键使用【PyCharm】远程连接Linux服务器【PyCharm】设置为中文界面 【PyCharm】远程连接Linux服务器 PyCharm 提供了远程开发的功能&#xff0c;使得开发者可以在本地编辑代码或使用服务器资源。 下面将详…...

InVideo AI技术浅析(五):生成对抗网络

一、特效生成 1. 工作原理 特效生成是计算机视觉中的高级应用,旨在通过算法生成高质量的视觉特效,如风格迁移、图像到图像的翻译等。InVideo AI 使用生成对抗网络(GAN)来实现这一功能。GAN 通过生成器和判别器两个网络的对抗训练,生成逼真的视觉特效。 2. 关键技术模型…...

Spring自定义BeanPostProcessor实现bean的代理

上文中&#xff1a;https://blog.csdn.net/qq_26437925/article/details/145241149 大致了解了spring aop的代理的实现&#xff0c;其实就是有个BeanPostProcessor代理了bean对象。 本文直接编写最简单的代码直观感受下 bean A: Service public class A {public A() {System.…...

【HF设计模式】06-命令模式

声明&#xff1a;仅为个人学习总结&#xff0c;还请批判性查看&#xff0c;如有不同观点&#xff0c;欢迎交流。 摘要 《Head First设计模式》第6章笔记&#xff1a;结合示例应用和代码&#xff0c;介绍命令模式&#xff0c;包括遇到的问题、采用的解决方案、遵循的 OO 原则、…...

Linux使用SSH连接GitHub指南

基础配置流程 步骤1:生成SSH密钥 打开终端:首先,打开你的Linux终端。 生成SSH密钥对:输入以下命令来生成一个新的SSH密钥对: ssh-keygen -t rsa -b 4096 -C "your_email@example.com"-t rsa:使用RSA加密算法生成密钥。-b 4096:密钥长度为4096位,增加安全性。…...

v2富文本框封装 @wangeditor/editor-for-vue

1 组件封装 <template><div class"editor-container"><div class"editor-wrapper"><Toolbarstyle"border-bottom: 1px solid #ccc":editor"editor":defaultConfig"toolbarConfig":mode"mode&quo…...

【分类】【损失函数】处理类别不平衡:CEFL 和 CEFL2 损失函数的实现与应用

引言 在深度学习中的分类问题中&#xff0c;类别不平衡问题是常见的挑战之一。尤其在面部表情分类任务中&#xff0c;不同表情类别的样本数量可能差异较大&#xff0c;比如“开心”表情的样本远远多于“生气”表情。面对这种情况&#xff0c;普通的交叉熵损失函数容易导致模型…...

AUTOSAR从入门到精通-自动驾驶测试技术

目录 前言 算法原理 测试场景定义与作用 测试场景要素 测试场景分类 场景信息提取与挖掘方法 自动驾驶感知测试分类 自动驾驶图像系统测试 自动驾驶激光雷达系统测试 自动驾驶融合感知系统测试 自动驾驶仿真测试 1. 功能安全 2. 预期功能安全 3. 软件测试 4.敏捷…...

优化大型语言模型的表达能力和依赖关系:理论

摘要 随着自然语言处理技术的发展&#xff0c;大型语言模型&#xff08;LLM&#xff09;已经成为理解和生成人类语言的强大工具。然而&#xff0c;如何有效提升这些模型的表达能力以及捕捉长距离依赖关系仍然是一个挑战。本文通过具体实例探讨了词表大小&#xff08;em_size&a…...

在Ubuntu下使用Wine运行MobaXterm并解决X服务器问题

MobaXterm是一款功能强大的终端模拟器&#xff0c;集成了SSH客户端和X服务器&#xff0c;常用于远程服务器管理。在Ubuntu下&#xff0c;我们可以通过Wine运行MobaXterm&#xff0c;同时解决X服务器问题&#xff0c;实现远程图形界面转发。这对于需要远程使用ROS&#xff08;如…...

【鸿蒙】0x02-LiteOS-M基于Qemu RISC-V运行

OpenHarmony LiteOS-M基于Qemu RISC-V运行 系列文章目录更新日志OpenHarmony技术架构OH技术架构OH支持系统类型轻量系统&#xff08;mini system&#xff09;小型系统&#xff08;small system&#xff09;标准系统&#xff08;standard system&#xff09; 简介环境准备安装QE…...

SW - 钣金零件保存成DWG时,需要将折弯线去掉

文章目录 SW - 钣金零件保存成DWG时&#xff0c;需要将折弯线去掉概述笔记备注END SW - 钣金零件保存成DWG时&#xff0c;需要将折弯线去掉 概述 如果做需要弯折的切割件&#xff0c;最好做成钣金零件。 最近做了几个小钣金(将钣金展开&#xff0c;建立新草图&#xff0c;在2…...

JAVA使用自定义注解,在项目中实现EXCEL文件的导出

首先定义一个注解 Retention(RetentionPolicy.RUNTIME) Target(ElementType.FIELD) public interface Excel {/*** 导出时在excel中排序*/int sort() default Integer.MAX_VALUE;/*** 导出到Excel中的名字.*/String name() default "";/*** 首行字段的批注*/String …...

【GIS操作】使用ArcGIS Pro进行海图的地理配准(附:墨卡托投影对比解析)

文章目录 一、应用场景二、墨卡托投影1、知识点2、Arcgis中的坐标系选择 三、操作步骤1、数据转换2、数据加载3、栅格投影4、地理配准 一、应用场景 地理配准是数字化之前必须进行的一项工作。扫描得到的地图数据通常不包含空间参考信息&#xff0c;需要通过具有较高位置精度的…...

flutter在使用gradle时的加速

当我使用了一些过时的插件的时候&#xff0c;遇到了一些问题 比如什么namespace 问题等&#xff0c;因为有些插件库没有更新了&#xff0c;或者最新版本处于测试阶段 于是我就删除这些旧插件(不符合我要求的插件) 于是根据各论坛的解决方法去做了以下的工作 1:项目中删除了这…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

微信小程序 - 手机震动

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

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

ServerTrust 并非唯一

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

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...