对于Muduo主从Reactor模式的理解

news2024/11/18 7:51:13

从12月20号开始看Muduo网络库,到28号的时候弄懂了EventLoop, Poller, Channel是怎么一回事,一番琢磨之后觉得还是应该发到博客上跟大家分享,特此记录。

对照linyacool那个webserver的实现,再看了一遍muduo的EventLoop, Poller ,Channel, 突然开悟了。原来这个架构不管是主线程mainreactor(Acceptor)还是工作线程subreactor都是靠EventLoop驱动的,每个EventLoop都手持一个Poller, EventLoop内部循环调用Poller::poll()监听各自channel数组上的事件,监听到活跃事件后就将活跃的channel包装成数组返回给EventLoop(其实就是模拟epoll),然后EventLoop遍历这个数组逐个触发其上事件,用channel封装fd的意义在于不同的channel对于同样的事件类型要注册不同的事件回调,比如同样是读事件,监听socket封装成的channel注册的读回调是接收连接,而客户端socket注册的读回调是读取请求。

(为打字方便,以下mainreactor一律称作主线程,subreactor一律称作工作线程)

void EventLoop::loop() {
  assert(!looping_);
  assert(isInLoopThread());
  looping_ = true;
  quit_ = false;
  // LOG_TRACE << "EventLoop " << this << " start looping";
  std::vector<SP_Channel> activeChannels;
  while (!quit_) {
    // cout << "doing" << endl;
    activeChannels.clear();
    activeChannels = poller_->poll();
    eventHandling_ = true;
    for (auto& it : activeChannels) it->handleEvents();
    eventHandling_ = false;
    doPendingFunctors();
    poller_->handleExpired();
  }
  looping_ = false;
}

Epoller::poll(){
    int nevent = epoll_wait();
    //..填充activeChannels
}

对于主线程只有一个监听socket包装成的acceptChannel, 这个channel在被EventLoop触发后接受连接,然后将连接socket按照round-robin算法分发给工作线程, 但是这里有个问题,就是主线程给工作线程分发连接的时候,工作线程很可能在忙于处理某个channel的请求,怎么办呢?

那只能延迟执行了,因此每个EventLoop都有一个pendingFunctors队列,pendingFunctors队列专门用于存放来自外部线程的"任务委托",EventLoop将活跃channels上事件都处理完了就会去处理这个队列,回到连接的例子上就是主线程分发连接时调用工作线程的queueInLoop()方法,将初始化连接的回调放入到队列中,等待着工作线程的EventLoop处理完请求后去清空这个队列。

void EventLoop::runInLoop(Functor&& cb) {
  if (isInLoopThread())
    cb();
  else
    queueInLoop(std::move(cb));
}

void EventLoop::queueInLoop(Functor&& cb) {
  {
    MutexLockGuard lock(mutex_);
    pendingFunctors_.emplace_back(std::move(cb));
  }

  if (!isInLoopThread() || callingPendingFunctors_) wakeup();
}

此外还要考虑一种情况,工作线程的Poller只能监听已经连接的channel,对于尚在pendingFunctors队列中的新连接的channel是poll()不到的, 这样要处理新连接,工作线程就只能等到其上poller的channel数组出现活跃,处理活跃channels的时候顺便doPendingFunctors()去处理新连接。

void EventLoop::doPendingFunctors() {
  std::vector<Functor> functors;
  callingPendingFunctors_ = true;

  {
    MutexLockGuard lock(mutex_);
    functors.swap(pendingFunctors_);
  }

  for (size_t i = 0; i < functors.size(); ++i) functors[i]();
  callingPendingFunctors_ = false;
}

那么假设工作线程很长一段时间得不到新事件,阻塞在poll()中,但是pendingFunctor队列中还挤压着任务要处理那怎么办呢?

比如工作线程正在调用doPendingFunctors()函数处理队列内的事件,但这时候主线程恰好调用queueInLoop()放了新的连接入队列(这里解释一下,为了避免竞态条件,doPendingFunctors的实现是准备一个临时队列,直接将pendingFunctors队列上的委托swap到这个临时队列上, 这样只需要在swap的时候加锁就行,减少临界区长度,加快效率)

工作线程注意不到,之后工作线程的的Poller上再也监测不到新事件,那么工作线程就阻塞在poll中无法处理队列上的新连接了,而且以后主线程还可能再调用queueInLoop()放新的连接进去,工作线程无法响应,怎么办?

因此EventLoop上又引入了一个wakeChannel和wakeup()方法,主线程调用工作线程EventLoop的queueInLoop()方法的时候也会调用其wakeup方法,往其wakeupChannel上写入一字节,打破poll()的僵局。这样连接阻塞的问题就解决了。(当然, wakeupChannel要加入epoll的监视树中)

void EventLoop::wakeup() {
  uint64_t one = 1;
  ssize_t n = writen(wakeupFd_, (char*)(&one), sizeof one);
  if (n != sizeof one) {
    LOG << "EventLoop::wakeup() writes " << n << " bytes instead of 8";
  }
}

有了如下理解,回头再看原书的时序图就清晰多了: img

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

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

相关文章

IDEA快速启动多个微服务模块 -idea如何开启Run DashBoard

文章目录 缘起 Run DashBoard面板如何开启开启 Run DashBoard 注意&#xff1a; 缘起 在idea里面如果需要启动多个项目的话&#xff0c;尤其是是比如微服务项目&#xff0c;动辄要启动五六个七八个应用&#xff0c;如果通过右上角那边启动会很不方便&#xff0c;你需要选择…

基于GIS简单处理世界土壤数据库(HWSD)的中国土壤数据集

来源&#xff1a;GIS前沿 一、 数据介绍 土壤属性表主要字段包括&#xff08;图1&#xff09;&#xff1a;详细描述请参考Harmonized World Soil Database (version 1.1).pdf文件&#xff0c;其中以T开头的土壤属性表示土壤上层的属性&#xff08;0-30cm&#xff09;&#xff…

【曲线全局逼近】

曲线全局逼近 本文是基于 这篇文章 翻译而来的&#xff0c;仅学习。 在插值中&#xff0c;插值曲线以给定的顺序通过所有给定的数据点。正如在全局插值页面中所讨论的&#xff0c;插值曲线可能会在所有数据点上摆动&#xff0c;而不是紧紧跟随数据多边形。为了克服这个问题&…

包装类的使用

文章目录一、单元测试方法的使用步骤二、包装类的使用基本数据类型、包装类、String类型之间的相互转化基本数据类型——>包装类注意包装类——>基本数据类型自动装箱与自动拆箱&#xff08;jdk5.0后&#xff09;基本数据类型、包装类——>String类型String类型——&g…

史上最全 Appium 自动化测试从基础到框架实战精华学习笔记(一)

1080402 31.8 KB 对测试人来说&#xff0c;Appium 是非常重要的一个开源跨平台自动化测试工具&#xff0c;它允许测试人员在不同的平台&#xff08;iOS、Android 等&#xff09;使用同一套 API 来写自动化测试脚本&#xff0c;这样可大幅提升代码复用率和工作效率。 本文汇总了…

郭盛华:警惕家庭智能扬声器中潜在的窃听风险

一名安全研究人员因识别Google Home智能扬声器中的安全问题而获得了107500美元的漏洞赏金&#xff0c;这些问题可能被用来安装后门并将其变成窃听设备。 国际知名网络黑客安全专家、东方联盟创始人郭盛华在一篇技术文章中透露&#xff1a;这些漏洞“允许无线附近的攻击者在设备…

服务的雪崩以及解决方案

文章目录一、什么是服务的雪崩二、服务雪崩形成的原因三、雪崩解决方案3.1 设置超时时间3.2 线程隔离&#xff08;舱壁模式&#xff09;3.3 熔断器&#xff08;断路器&#xff09;3.4 限流四、总结一、什么是服务的雪崩 服务的雪崩效应是一种因服务提供者不可用导致服务调用者…

从源码角度带你清楚分析Spring 的Lazy-init 延迟加载机制原理

lazy-init 延迟加载应用 ApplicationContext 容器的默认值行为是在启动服务器时将所有Singleton Bean 提前进行实例,提前实例化意味着作为初始化过程的一部分,ApplicationContext 实例会创建并配置所有的singleton Bean. 例如: <bean id"testBean" class"c…

张力控制PID增益(Kp)自适应算法详解(含SCL和梯形图完整源代码)

有关收放卷张力控制的详细内容,请参看下面的文章链接,这里不再赘述。 变频器简单张力控制(线缆收放卷应用)_RXXW_Dor的博客-CSDN博客张力控制的开闭环算法,可以查看专栏的其它文章,链接地址如下:PLC张力控制(开环闭环算法分析)_RXXW_Dor的博客-CSDN博客。https://blo…

ThinkPHP3.2.3_SQLi

文章目录ThinkPHP3.2.3_SQLi0x00 测试代码0x02 paylaod0x03 调用分析0x04 漏洞成因0x05 总结ThinkPHP3.2.3_SQLi 刚好有朋友在测一个目标是tp3.2.3框架的站遇到了一些问题 顺手跟一下流程复现一下吧。 0x00 测试代码 <?php namespace Home\Controller; use Think\Contr…

启岁新程|跨越2022,2023一起追光而行!

和气渐入东风&#xff0c;岁杪将迎春临&#xff0c; 时间的车轮即将驶离2022开往2023&#xff0c; 回首来时路&#xff0c;我们收获满满&#xff0c; 展望新征程&#xff0c;我们斗志昂扬...... 2022注定是不平凡的一年&#xff0c; 在这一年里&#xff0c; 我们与行业同行…

深度学习:07 CNN经典模型总结(LeNet-5、AlexNet、VGG、GoogLeNet、ResNet)

目录 CNN经典网络模型 LeNet-5 AlexNet VGG GoogLeNet (Inception) ResNet 如何选择网络 CNN经典网络模型 以下介绍了LeNet-5、AlexNet、VGG、GoogLeNet、ResNet等&#xff0c;它们通常用于图像的数据处理&#xff0c;那么卷积神经网络是否应用于自然语言分类任务呢&am…

.net可视化表单设计工具

在很多软件系统中&#xff0c;表单开发都是很重要的一个部分。在表单开发中&#xff0c;往往会遇到重复开发的问题&#xff0c;例如在页面搭建系统中&#xff0c;除了组件本身的逻辑&#xff0c;配置组件数据的表单通常也需要开发人员重复手动开发。这就导致开发人员不仅要维护…

VS1053音频解码器介绍

VS1053音频解码器简介 VS1053b是单片Ogg Vorbis/MP3/AAC/WMA/MIDI音频解码器&#xff0c;及IMA ADPCM 编码器和用户加载的Ogg Vorbis编码器。它包含了一个高性能、有专利的低功耗DSP处理器内核VS_DSP、工作数据存储器、供用户应用程序和任何固化解码器一起运行的16 KiB 指令RAM…

小程序:后台交互-首页

目录 一&#xff0c;数据库准备 二&#xff0c;后台准备 pom.xml 配置数据源 mybatis-generator 整合mybatis 三&#xff0c;准备前端首页的数据 Promise 封装request 会议展示 四&#xff0c;通过wxs将首页动态数据优化 一&#xff0c;数据库准备 二&#xff0c;后…

docker的镜像存放地址

今天突然想到一个问题&#xff0c;docker的镜像到底存在哪的&#xff0c;一直没太注意&#xff0c;稍微记录下 1、先查看下本地有哪些images docker images 2、找到docker的数据目录 /var/lib/docker 可以看到有好多的目录 盲猜一波大概也能猜到 containers 是当前运行的容器…

React-Router6路由相关一(路由的基本使用、重定向、NavLink·、路由表、嵌套路由)(七)

系列文章目录 第一章&#xff1a;React基础知识&#xff08;React基本使用、JSX语法、React模块化与组件化&#xff09;&#xff08;一&#xff09; 第二章&#xff1a;React基础知识&#xff08;组件实例三大核心属性state、props、refs&#xff09;&#xff08;二&#xff0…

Android OpenGL ES 学习(十二) - MediaCodec + OpenGL 解析H264视频+滤镜

OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投影 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用…

宝妈可以做什么副业比较好?盘点七种适合宝妈的在家工作

现在有很多全职宝妈在家带孩子的&#xff0c;除了带孩子以外呢&#xff0c;还有很多的空闲时间&#xff0c;所以找一份自由的兼职工作是如今很多宝妈的想法。不仅可以给家里增加收入&#xff0c;支付日常开支&#xff0c;减轻老公的压力&#xff0c;还可以让自己学点新的赚钱知…

Linux编辑器-vim

一、vim的基本概念 我们讲解vim的三种模式(其实有好多模式&#xff0c;目前掌握这3种即可),分别是命令模式&#xff08;command mode&#xff09;、插入模式&#xff08;Insert mode&#xff09;和底行模式&#xff08;last line mode&#xff09;&#xff0c;各模式的功能区分…