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;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/899994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UE5学习笔记 FPS游戏制作38 继承标准UI

文章目录 UE的UIUMG的继承继承标准控件创建标准控件继承标准控件的用处 UE的UI 和Untiy有onGui和UGui类似&#xff0c;UE有slateUI和UMG,slateUI是早期只能用C编写的UI&#xff0c;UMG是现在使用的&#xff0c;可以拖拽编辑的UI slateUI是UMG的父类 UMG的继承 我们编写一个控…

C#核心学习(七)面向对象--封装(6)C#中的拓展方法与运算符重载: 让代码更“聪明”的魔法

目录 一、什么是拓展方法&#xff1f; 二、拓展方法有啥用&#xff1f;怎么写拓展方法&#xff1f; 1. ​核心用途 2. ​编写步骤 实现步骤 关键点说明 关键规则 3. ​注意事项 三、什么是运算符重载&#xff1f; 四、运算符重载有啥用&#xff1f;怎么写&#xff1f;…

银行卡归属地查询API接口如何对接?

银行卡归属地查询 API 接口是一种能让开发者通过编程方式获取银行卡归属地等相关信息的工具。借助此接口&#xff0c;开发者可将银行卡归属地查询功能集成到自己的应用程序或系统里&#xff0c;像电商平台、第三方支付公司等都能运用它来提升业务的准确性与安全性。 银行卡归属…

ORM mybits mybits-plus

ORM ORM 即对象关系映射&#xff08;Object Relational Mapping&#xff09;&#xff0c;是一种程序设计技术&#xff0c;用于实现面向对象编程语言里不同类型系统的数据之间的转换。下面从基本概念、工作原理、优势与劣势、常见的 ORM 框架等方面详细介绍 ORM。 常见的orm框架…

网络编程—网络概念

目录 1 网络分类 1.1 局域网 1.2 广域网 2 常见网络概念 2.1 交换机 2.2 路由器 2.3 集线器 2.4 IP地址 2.5 端口号 2.6 协议 3 网络协议模型 3.1 OSI七层模型 3.2 TCP/IP五层模型 3.3 每层中常见的协议和作用 3.3.1 应用层 3.3.2 传输层 3.3.3 网络层 3.3.4…

4月3日工作日志

一个朴实无华的目录 今日学习内容&#xff1a;1.关系数据库 今日学习内容&#xff1a; 1.关系数据库

git commit Message 插件解释说明

- feat - 一项新功能 - fix - 一个错误修复 - docs - 仅文档更改 - style - 不影响代码含义的更改&#xff08;空白、格式化、缺少分号等&#xff09; - refactor - 既不修复错误也不添加功能的代码更改 - perf - 提高性能的代码更改 - build - 影响构建系统或外部依赖项…

ngx_open_file

定义在 src\os\unix\ngx_files.h #define ngx_open_file(name, mode, create, access) \open((const char *) name, mode|create, access) name&#xff1a;文件名&#xff08;通常是一个字符串&#xff09;。mode&#xff1a;文件打开模式&#x…

23种设计模式-行为型模式-责任链

文章目录 简介问题解决代码核心改进点&#xff1a; 总结 简介 责任链是一种行为设计模式&#xff0c;允许你把请求沿着处理者链进行发送。收到请求后&#xff0c;每个处理者均可对请求进行处理&#xff0c;或将其传递给链上的下个处理者。 问题 假如你正在开发一个订单系统。…

注意力机制在大语言模型中的原理与实现总结

注意力机制在大语言模型中的原理与实现总结 1. 章节介绍 在大语言模型的学习中&#xff0c;理解注意力机制至关重要。本章节旨在深入剖析注意力机制的原理及其在大语言模型中的应用&#xff0c;为构建和优化大语言模型提供理论与实践基础。通过回顾神经网络基础及传统架构的局…

kafka消息可靠性传输语义

Kafka提供了多种消息传递语义&#xff0c;以适应不同的业务需求和可靠性要求。以下是Kafka消息传输的可靠性语义及其实现机制&#xff1a; 1. At Most Once&#xff08;至多一次&#xff09; 语义&#xff1a;消息可能会丢失&#xff0c;但不会被重复传递。 实现机制&#xf…

NLP高频面试题(三十三)——Vision Transformer(ViT)模型架构介绍

Transformer架构在自然语言处理领域取得了显著成功&#xff0c;激发了研究人员将其应用于计算机视觉任务的兴趣。Vision Transformer&#xff08;ViT&#xff09;应运而生&#xff0c;成为图像分类等视觉任务中的新兴架构。本文将介绍ViT的基本架构、工作原理&#xff0c;并与传…

Oracle数据库数据编程SQL<3.6 PL/SQL 包(Package)>

包是Oracle数据库中一种重要的PL/SQL程序结构,它将逻辑相关的变量、常量、游标、异常、过程和函数组织在一起,提供了更好的封装性和模块化。在大型项目中,可能有很多模块,而每一个模块又有自己的存过、函数等。而这些存过、函数默认是放在一起的,如果所有的存过函数都是放…

机器学习 分类算法

【实验名称】 实验&#xff1a;分类算法 【实验目的】 1.了解分类算法理论基础 2.平台实现算法 3. 编程实现分类算法 【实验原理】 分类(Categorization or Classification)就是按照某种标准给对象贴标签(label),再根据标签来区分归类。 【实验环境】 OS&#xff1a;Ubuntu16.0…

HTML5 Canvas绘画板项目实战:打造一个功能丰富的在线画板

HTML5 Canvas绘画板项目实战&#xff1a;打造一个功能丰富的在线画板 这里写目录标题 HTML5 Canvas绘画板项目实战&#xff1a;打造一个功能丰富的在线画板项目介绍技术栈核心功能实现1. 画板初始化与工具管理2. 多样化绘画工具3. 事件处理机制 技术要点分析1. Canvas上下文优化…

【YOLOv8】YOLOv8改进系列(12)----替换主干网络之StarNet

主页&#xff1a;HABUO&#x1f341;主页&#xff1a;HABUO &#x1f341;YOLOv8入门改进专栏&#x1f341; &#x1f341;如果再也不能见到你&#xff0c;祝你早安&#xff0c;午安&#xff0c;晚安&#x1f341; 【YOLOv8改进系列】&#xff1a; YOLOv8结构解读 YOLOv8…

1Panel 面板 宝塔面板 Ubuntu 24.04

1Panel 面板 宝塔面板 Ubuntu 24.04 https://1panel.cn/ 1Panel 是一款开源的 Linux 服务器运维管理面板&#xff0c;它就像是给服务器配上了一个智能管家&#xff0c;让我们能通过 Web 端轻松管理服务器。以往我们管理 Linux 服务器&#xff0c;常常需要在命令行中输入各种复…

Node.js全局生效的中间件

目录 1. 目录结构 2. 代码实现 2.1 安装Express 2.2 app.js - 主文件 2.3 globalMiddleware.js - 全局中间件 3. 程序运行结果 4. 总结 在Node.js的Express框架中&#xff0c;全局生效的中间件是指应用程序启动后&#xff0c;对所有请求都有效的中间件。它通常用于日志记…

WiFi(无线局域网)技术的多种工作模式

WiFi&#xff08;无线局域网&#xff09;技术支持多种工作模式&#xff0c;以满足不同的网络需求和应用场景。以下是主要的WiFi工作模式及其详细说明&#xff1a; 1. 基础设施模式&#xff08;Infrastructure Mode&#xff09; [无线接入点 (AP)]/ | \ [客户端…

PHP 8.x:现代Web开发的性能与效率革命

随着PHP 8.x系列的持续演进&#xff0c;这门诞生于1995年的“古老”语言正焕发新生。通过引入革命性的JIT编译器、类型系统增强及一系列现代化语法特性&#xff0c;PHP 8.x不仅巩固了其在Web开发领域的统治地位&#xff0c;更将性能与开发者体验推向新高度。 一、JIT编译器&am…