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

Muduo网络库实现 [九] - EventLoopThread模块

目录

设计思路

类的设计

模块的实现

私有接口

公有接口 


设计思路

我们说过一个EventLoop要绑定一个线程未来该EventLoop所管理的所有的连接的操作都需要在这个EventLoop绑定的线程中进行,所以我们该如何实现将EventLoop和线程绑定呢?

按照我们前面实现的EventLoop的逻辑,构造函数的时候,_thread_id(std::this_thread::get_id()) 也就是EventLoop构造时就直接绑定了当前线程了,那么我们就需要先创建线程,然后在线程的入口函数中来创建一个EventLoop对象,这样我们的EventLoop对象就和一个线程绑定了,创建完之后它可以将这个EventLoop的指针返回给TcpServer用来分配给新连接进行关联。

那么我们可不可以先创建一批EventLoop对象再来绑定线程呢?

从技术的角度来说当然可以,我们只需要设置一个接口用来设置EventLoop的_thread_id就行了,但是这样做会存在一个问题:当我们创建一批EventLoop的时候,再创建一批线程来进行绑定之前,是有一个时间窗口 的,在这个时间窗口中,如果有新连接到来,且有新事件到来,那么就会出现问题。

就好比一家繁忙的餐厅,你是经理需要分配服务员(线程)负责特定的餐桌区域(EventLoop)。正确的做法是:先雇佣服务员,培训他们,然后才开门营业接待客人。但如果你决定先开门营业,让餐桌区域准备好接待客人,然后才开始招聘和分配服务员,就会出现危险的时间窗口:

客人已经入座点餐(连接已建立),甚至食物已经准备好(事件已到来),但没有服务员知道这些餐桌是他们负责的!结果就是客人坐在那里等待,没人来服务他们,食物在厨房里变冷,而餐厅陷入混乱。

也就是我们的_thread_id还没有切换到我们想要的线程id上,还在创建EventLoop的线程中,那么这时候操作就是由这个创建EventLoop的线程执行了,而后续切换线程之后,又会由新的线程来执行操作,那么就会出现一个连接的所有操作并不在一个线程中全部完成,可能会出现在多个线程中执行的情况,

再想象这样一个场景:餐厅开始营业,最初由经理(创建EventLoop的线程)临时担任服务员的角色,开始接待客人和处理订单。然后在客人就餐过程中,经理突然告诉新来的服务员:"从现在开始,这些桌子由你负责了",然后经理离开去做其他工作。

这会导致严重的混乱:

  • 服务员不知道这些客人已经点了什么菜
  • 不清楚客人的特殊要求或过敏信息
  • 不了解客人的用餐进度
  • 甚至可能重复上菜或忘记上某些菜品

客人的体验会非常糟糕,因为他们的服务被分割在两个不同的服务人员之间,没有连贯性和一致性。

这是我们不想看到的,我们要确保一个连接的所有操作都在一个线程中执行,所以我们不能采取这种方案。

那么我们的方案:就只能是先创建线程再创建EventLop对象,后续会通过特定的方式返回这个EventLoop的指针交给TcpServer进行分配。

为了方便操作,我们可以设置一个新的模块,也就是EventLoopThread模块,专门用于创建一个线程并创建绑定一个EventLoop。

它内部有两个成员,一个就是我们的线程对象,另一个就是我们的EventLoop的指针。

所以我们要设置线程入口函数,然后在线程入口函数中创建一个EventLoop对象,创建完之后执行EventLoop的Start来完成事件的循环。

但是这时候就会有一个问题,因为线程的创建到创建好一个EventLoop对象并设置指针变量是有一个时间窗口的,同时,在设置我们的指针成员的同时,有可能由其他的线程需要获取这个指针,那么就会出现读写并发的情况,或者说这个指针变量会有线程安全问题。

我们还拿餐厅的例子进行加以理解

想象餐厅正在准备开业经理在招聘并培训新的服务员。每个服务员需要熟悉自己的工作区域、学习餐厅系统并拿到自己的工作牌(相当于创建EventLoop并设置线程ID的过程)。

这时候,另一位经理(其他线程)已经开始在前台接待客人,并试图将客人分配给"正在培训中"的服务员。但问题是,这些服务员可能还没有完成培训,没有拿到工作牌,甚至可能还没有被正式雇佣!

那么我们需要保护这个指针指针变量的互斥与同步访问,需要使用一个互斥锁和一个条件变量来保证指针的安全。

在餐厅中,解决方案也是使用一个协调板和一个明确的流程:

  • 招聘和培训完全结束后,才将服务员的信息放到协调板上
  • 前台经理只查看协调板上已确认可用的服务员
  • 使用一个信号系统(如专门的管理员)确保协调板的更新和查看不会同时发生

类的设计

综上我们知道了EventLoop的流程

  • 首先,EventLoopThread对象被创建(可能在主线程或其他线程中)
  • 当调用EventLoopThread的构造方法时,它会创建一个新的线程
  • 这个新线程开始执行StartRoutine()函数
  • 在StartRoutine()函数内部,线程创建一个新的EventLoop对象
  • 线程将这个EventLoop对象的指针安全地赋值给共享变量_loop
  • 通知等待的线程EventLoop已经创建完成
  • 开始运行EventLoop的事件循环

那么EventLoopThread类的成员如下:

class EventLoopThread
{
private:EventLoop* _loop;std::thread _thread;std::mutex _mutex;      //保护_loop安全std::condition_variable _cond;  //实现同步
private:
//线程的入口函数void StartRoutine();
public: EventLoopThread(){}//提供一个接口用于获取内部的EventLoop//意味着这个_loop会被多个线程竞争,那么需要锁和条件变量来实现同步互斥//因为未来线程刚创建的时候,在还没有创建好EventLoop对象的时候,这时候就可能会被主线程或者其他线程来获取Loop了,那么这时候是线程不安全的,所以需要加锁保护//同时,为了防止线程中的EventLoop对象还没创建就有线程来获取,我们需要再使用一个条件变量。 申请到锁之后,如果条件不满足,线程还需要在条件变量下等待,直到条件满足再来竞争锁并获取锁EventLoop* GetEventLoop();
};

模块的实现

入口函数很简单,无非就是加锁创建完EventLoop对象之后,唤醒在条件变量下等待的线程,然后就开始执行EventLoop的Start循环逻辑。

私有接口

    //线程的入口函数void StartRoutine(){//加锁创建对象EventLoop* loop = new EventLoop();{std::unique_lock<std::mutex> lock(_mutex);_loop = loop;}//唤醒条件变量下的线程_cond.notify_all();//启动EventLoop循环loop -> Start();}

公有接口 

然后就是构造函数,无非就是初始化_loop和设置thread的入口函数

    EventLoopThread():_loop(nullptr),_thread(std::bind(&EventLoopThread::StartRoutine, this)) // 创建一个新线程,并指定StartRoutine作为线程的入口函数{}

剩下的就是一个获取EventLoop的接口了,其实无非就是加锁和条件变量下等待这两个步骤:

    EventLoop* GetEventLoop(){EventLoop* ret = nullptr;{std::unique_lock<std::mutex> lock(_mutex); //加锁_cond.wait(lock,[&](){return _loop!=nullptr;}); //判断函数返回值为真//走到这里说明被唤醒了ret = _loop;}return ret;}

相关文章:

Muduo网络库实现 [九] - EventLoopThread模块

目录 设计思路 类的设计 模块的实现 私有接口 公有接口 设计思路 我们说过一个EventLoop要绑定一个线程&#xff0c;未来该EventLoop所管理的所有的连接的操作都需要在这个EventLoop绑定的线程中进行&#xff0c;所以我们该如何实现将EventLoop和线程绑定呢&#xff1f;…...

Vim操作指令全解析

Vim是我们在Linux日常工作中不可或缺的文本编辑器。它强大的功能和高效的编辑方式可以极大提升工作效率。本文将全面解析Vim的各种操作指令&#xff0c;从基础操作到高级技巧。 一、Vim模式解析 Vim是一个模式化编辑器&#xff0c;理解不同模式是掌握Vim的关键&#xff1a; …...

《K230 从熟悉到...》识别机器码(AprilTag)

《K230 从熟悉到...》识别机器码&#xff08;aprirltag&#xff09; tag id 《庐山派 K230 从熟悉到...》 识别机器码&#xff08;AprilTag&#xff09; AprilTag是一种基于二维码的视觉标记系统&#xff0c;最早是由麻省理工学院&#xff08;MIT&#xff09;在2008年开发的。A…...

VMware ESXi:企业级虚拟化平台详解

VMware ESXi&#xff1a;企业级虚拟化平台详解 目录 什么是VMware ESXi&#xff1f; ESXi的发展历史 ESXi的核心特性 3.1 裸机架构&#xff08;Type-1 Hypervisor&#xff09; 3.2 轻量化与高性能 3.3 集中管理&#xff08;vCenter集成&#xff09; ESXi的架构与工作原理…...

使用 PyTorch 的 `optim.lr_scheduler.CosineAnnealingLR` 学习率调度器

使用 PyTorch 的 optim.lr_scheduler.CosineAnnealingLR 学习率调度器 在深度学习中,学习率(Learning Rate, LR)是影响模型训练效果的一个关键超参数。一个合适的学习率调度策略可以帮助模型更快地收敛,同时避免陷入局部最优或振荡。PyTorch 提供了多种学习率调度器,其中…...

栈和队列的概念

1.栈的概念 只允许在固定的一端进行插入和删除&#xff0c;进行数据的插入和数据的删除操作的一端数栈顶&#xff0c;另一端称为栈底。 栈中数据元素遵循后进先出LIFO (Last In First Out) 压栈&#xff1a;栈的插入。 出栈&#xff1a;栈的删除。出入数据在栈顶。 那么下面…...

常用的元素操作API

click 触发当前元素的点击事件 clear() 清空内容 sendKeys(...) 往文本框一类元素中写入内容 getTagName() 获取元素的的标签名 getAttribute(属性名) 根据属性名获取元素属性值 getText() 获取当前元素的文本值 isDisplayed() 查看元素是否显示 get(String url) 访…...

红日靶场一实操笔记

一&#xff0c;网络拓扑图 二&#xff0c;信息搜集 1.kali机地址&#xff1a;192.168.50.129 2.探测靶机 注&#xff1a;需要win7开启c盘里面的phpstudy的服务。 nmap -sV -Pn 192.168.50.128 或者扫 nmap -PO 192.168.50.0/24 可以看出来win7(ip为192.168.50.128)的靶机开…...

SpringBoot集成Redis 灵活使用 TypedTuple 和 DefaultTypedTuple 实现 Redis ZSet 的复杂操作

以下是 Spring Boot 集成 Redis 中 TypedTuple 和 DefaultTypedTuple 的详细使用说明&#xff0c;包含代码示例和场景说明&#xff1a; 1. 什么是 TypedTuple 和 DefaultTypedTuple&#xff1f; TypedTuple<T> 接口&#xff1a; 定义了 Redis 中有序集合&#xff08;ZSet…...

7-4 BCD解密

BCD数是用一个字节来表达两位十进制的数&#xff0c;每四个比特表示一位。所以如果一个BCD数的十六进制是0x12&#xff0c;它表达的就是十进制的12。但是小明没学过BCD&#xff0c;把所有的BCD数都当作二进制数转换成十进制输出了。于是BCD的0x12被输出成了十进制的18了&#x…...

Golang改进后的任务调度系统分析

以下是整合了所有改进点的完整代码实现: package mainimport ("bytes""context""fmt""io""log""net/http""sync""time""github.com/go-redis/redis/v8""github.com/robfig/…...

【目标检测】【深度学习】【Pytorch版本】YOLOV2模型算法详解

【目标检测】【深度学习】【Pytorch版本】YOLOV2模型算法详解 文章目录 【目标检测】【深度学习】【Pytorch版本】YOLOV2模型算法详解前言YOLOV2的模型结构YOLOV2模型的基本执行流程YOLOV2模型的网络参数YOLOV2模型的训练方式 YOLOV2的核心思想前向传播阶段反向传播阶段 总结 前…...

NineData云原生智能数据管理平台新功能发布|2025年3月版

本月发布 15 项更新&#xff0c;其中重点发布 3 项、功能优化 11 项、性能优化 1 项。 重点发布 基础服务 - MFA 多因子认证 新增 MFA 多因子认证&#xff0c;提升账号安全性。系统管理员开启后&#xff0c;所有组织成员需绑定认证器&#xff0c;登录时需输入动态验证码。 数…...

破局与赋能:信息系统战略规划方法论

信息系统战略规划是将组织的战略目标和发展规划转化为信息系统的战略目标和发展规划的过程&#xff0c;常见的方法有以下几种&#xff1a; 一、企业系统规划法&#xff08;BSP&#xff09; 1.基本概念&#xff1a;通过全面调查&#xff0c;分析企业信息需求&#xff0c;确定信…...

GLSL(OpenGL 着色器语言)基础语法

GLSL&#xff08;OpenGL 着色器语言&#xff09;基础语法 GLSL&#xff08;OpenGL Shading Language&#xff09;是 OpenGL 计算着色器的语言&#xff0c;语法类似于 C 语言&#xff0c;但提供了针对 GPU 的特殊功能&#xff0c;如向量运算和矩阵运算。 着色器的开头总是要声明…...

Redis基础知识-3

RedisTemplate对多种数据结构的操作 1. String类型 示例代码&#xff1a; // 保存数据 redisTemplate.opsForValue().set("user:1001", "John Doe"); // 设置键值对&#xff0c;无过期时间 redisTemplate.opsForValue().set("user:1002", &qu…...

Git Rebase 操作中丢失提交的恢复方法

背景介绍 在团队协作中,使用 Git 进行版本控制是常见实践。然而,有时在执行 git rebase 或者其他操作后,我们可能会发现自己的提交记录"消失"了,这往往让开发者感到恐慌。本文将介绍几种在 rebase 后恢复丢失提交的方法。 问题描述 当我们执行以下操作时,可能…...

【diffusers 进阶(十五)】dataset 工具,Parquet和Arrow 数据文件格式,load dataset 方法

系列文章目录 【diffusers 极速入门&#xff08;一&#xff09;】pipeline 实际调用的是什么&#xff1f; call 方法!【diffusers 极速入门&#xff08;二&#xff09;】如何得到扩散去噪的中间结果&#xff1f;Pipeline callbacks 管道回调函数【diffusers极速入门&#xff0…...

unity各个面板说明

游戏开发&#xff0c;unity各个面板说明 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是Python基础语法。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性&#xff0c;希望对您有用~ unity简介-unity基础…...

游戏引擎学习第199天

回顾并发现我们可能破坏了某些东西 目前&#xff0c;我们的调试 UI 运行得相对顺利&#xff0c;可以创建可修改的调试变量&#xff0c;也可以插入分析器&#xff08;profiler&#xff09;等特殊视图组件&#xff0c;并进行一些交互操作。然而&#xff0c;在上一次结束时&#…...

Linux红帽:RHCSA认证知识讲解(十)使用 tar创建归档和压缩文件

Linux红帽&#xff1a;RHCSA认证知识讲解&#xff08;十&#xff09;使用 tar创建归档和压缩文件 前言一、归档与压缩的基本概念1.1 归档与压缩的区别 二、使用tar创建归档文件2.1 tar命令格式2.2 示例操作 三、使用tar进行压缩3.2 命令格式3.3 示例操作 前言 在红帽 Linux 系…...

端到端机器学习流水线(MLflow跟踪实验)

目录 端到端机器学习流水线(MLflow跟踪实验)1. 引言2. 项目背景与意义2.1 端到端机器学习流水线的重要性2.2 MLflow的作用2.3 工业级数据处理需求3. 数据集生成与介绍3.1 数据集构成3.2 数据生成方法4. 机器学习流水线与MLflow跟踪4.1 端到端机器学习流水线4.2 MLflow跟踪实验…...

相平面案例分析爱情故事

动态系统的分析可以分为三个步骤&#xff1a;第一步描述系统&#xff0c;通过语言来描述系统的特性&#xff0c;第一步描述系统&#xff0c;即通过语言来描述系统的特性&#xff1b;第二步数学分析&#xff0c;即使用数学工具对系统进行量化解析&#xff1b;第三步结果与讨论&a…...

《2024年全球DDoS攻击态势分析》

从攻击态势来看&#xff0c;2024年DDoS攻击频次继续呈增长趋势&#xff0c;2024年同步增加1.3倍&#xff1b;超大规模攻击激增&#xff0c;超800Gbps同比增长3.1倍&#xff0c;累计高达771次&#xff0c;且互联网史上最大带宽和最大包速率攻击均被刷新&#xff1b;瞬时泛洪攻击…...

RTC实时时钟M41T11M6F国产替代FRTC4111S

由NYFEA徕飞公司制造的FRTC4111S是一种低功耗的串行实时时钟(RTC)&#xff0c;国产直接替代ST的M41T11M6F,其具有56字节的NVRAM&#xff0c;32.768 kHz振荡器(由外部晶体控制)和RAM的前8字节用于时钟/日历功能并以二进制编码十进制(BCD)格式配置。地址和数据通过两行双向总线串…...

Uni-app PDF Annotation plugin library online API examples

This article introduces the online version of the ElasticPDF API tutorial for the PDF annotation plug-in library in Uni-app projects. The API includes ① Export edited PDF data; ② Export annotations json data; ③ Reload old annotations; ④ Change files; ⑤…...

SpringKafka消息发布:KafkaTemplate与事务支持

文章目录 引言一、KafkaTemplate基础二、消息序列化三、事务支持机制四、错误处理与重试五、性能优化总结 引言 在现代分布式系统架构中&#xff0c;Apache Kafka作为高吞吐量的消息系统&#xff0c;被广泛应用于事件驱动应用开发。Spring Kafka为Java开发者提供了与Kafka交互…...

进行性核上性麻痹护理指南,助患者安稳生活

生活细致照料 安全保障&#xff1a;进行性核上性麻痹患者易出现平衡障碍、步态不稳&#xff0c;居家环境需格外留意安全。移除地面障碍物&#xff0c;保持通道畅通&#xff0c;在卫生间、走廊安装扶手&#xff0c;防止患者摔倒受伤。 饮食协助&#xff1a;患者常伴有吞咽困难&…...

提取嘉立创3D封装

嘉立创上元器件基本都有3D封装&#xff0c;当用AD或其他软件画PCB时&#xff0c;需要用到的3D封装可以从嘉立创EDA中提取。 首先新建工程&#xff0c;然后放置要提取3D封装的器件 导出-》3D文件 因为导出的文件中包含器件的3D封装和PCB板&#xff0c;需要把PCB板删除才能使用…...

工作记录 2017-03-24

工作记录 2017-03-24 序号 工作 相关人员 1 修改了邮件上的问题。 更新RD服务器。 郝 更新的问题 1、修改了New User时 init的保存。 2、文件的查询加了ID。 3、加了 patient insurance secondary 4、修改了payment detail的处理。 识别引擎监控 Ps (iCDA LOG :剔除…...