一. Reactor模式简介

  1. Reactor释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。

二. moduo库Reactor模式的实现

muduo主要通过3个类来实现Reactor模式:EventLoop,Channel,Poller。

1. EventLoop

  1. 事件循环。moduo的线程模型为one loop per thread,即每个线程只能有一个EventLoop对象。EventLoop对象的生命周期通常和其所属的线程一样长。

数据成员:

const pid_t threadId_;保存当前EventLoop所属线程id

boost::scoped_ptr poller_; 实现I/O复用 boost::scoped_ptr timerQueue_;

int wakeupFd_;

       boost::scoped_ptr wakeupChannel_; 用于处理wakeupFd_上的可读事件,将事件分发到handlRead() ChannelList activeChannels_; 有事件就绪的              Channel Channel* currentActiveChannel_;

       MutexLock mutex_; pendingFunctors_回暴露给其他线程,所以需要加锁 std::vectorpendingFunctors_; 

主要功能函数:

loop(),在该函数中会循环执行以下过程:调用Poller::poll(),通过此调用获得一个vector<channel*>activeChannels_的就绪事件集合,再遍历该容器,执行每个Channel的Channel::handleEvent()完成相应就绪事件回调,最后执行pendingFunctors_排队的函数。上述一次循环就是一次Reactor模式完成。

runInLoop(boost::function<void()>),实现用户指定任务回调,若是EventLoop隶属的线程调用EventLoop::runInLoop()则EventLoop马上执行;若是其它线程调用则执行EventLoop::queueInLoop(boost::function<void()>将任务添加到队列中(线程转移)。EventLoop如何获得有任务这一事实呢?通过eventfd可以实现线程间通信,具体做法是:其它线程向EventLoop::vector<boost::function<void()> >添加任务T,然后通过EventLoop::wakeup()向eventfd写一个int,eventfd的回调函数EventLoop::handleRead()读取这个int,从而相当于EventLoop被唤醒,此时loop中遍历队列执行堆积的任务。这里采用Channel管理eventfd,Poller侦听eventfd体现了eventfd可以统一事件源的优势。

queueInLoop(Functor& cb),将cb放入队列,并在必要时唤醒IO线程。有两种情况需要唤醒IO线程,1 调用queueInLoop()的线程不是IO线程,2 调用queueInLoop()的线程是IO线程,而此时正在调用pengding functor。

2. Channel

  1. 事件分发器。每个Channel只属于一个EventLoop,每个Channel只负责一个文件描述符fdIO事件分发,但其不拥有fd

数据成员:

int fd_文件描述符,

int events_ 文件描述符注册事件,

int revents_文件描述符的就绪事件,由Poller::poll设置

readCallback_,writeCallback...各种事件回调,会在拥有该Channel类的构造函数中被注册,例如TcpConnction会在构造函数中TcpConnection::handlRead()注册给Channel::readCallback

主要功能函数:

setCallback()系列函数,接受Channel所属的类注册相应的事件回调函数

     enableReading(),update(), 当一个fd想要注册可读事件时,首先通过Channel::enableReading()-->Channel::update(this)->EventLoop::updateChannel(Channel)->Poller::updateChannel(Channel*)调用链向poll系统调用的侦听事件表注册或者修改注册事件。

handleEvent(), Channel作为是事件分发器其核心结构是Channel::handleEvent(),该函数调用Channel::handleEventWithGuard(),在其内根据Channel::revents的值分发调用相应的事件回调。

3. Poller

  1. PollerIO multiplexing的封装,封装了pollepollPollerEventLoop的间接成员,只供拥有该PollerEventLoopIO线程调用。生命期与EventLoop相等。

数据成员:

vector pollfds_事件结构体数组用于poll的第一个参数;

map<int,channel*> channels_用于文件描述符fd到Channel的映射便于快速查找到相应的Channel

主要功能函数:

updateChannel(Channel*) 用于将传入的Channel关心的事件注册给Poller。

poll(int timeoutMs,vector<channel*> activeChannels)其调用poll侦听事件集合,将就绪事件所属的Channel调用fillActiveChannels()加入到activeChannels_中。

其他类

EventLoopThread: 启动一个线程执行一个EventLoop,其语义和"one loop per thread“相吻合。注意这里用到了互斥量和条件变量,这是因为线程A创建一个EventLoopThread对象后一个运行EventLoop的线程已经开始创建了,可以通过EventLoopThread::startLoop()获取这个EventLoop对象,但是若EventLoop线程还没有创建好,则会出错。所以在创建EventLoop完成后会执行condititon.notify()通知线程A,线程A调用EventLoopThread::startLoop()时调用condition.wai()等待,从而保证获取一个创建完成的EventLoop.毕竟线程A创建的EventLoop线程,A可能还会调用EventLoop执行一些任务回调呢。

转自:http://www.cnblogs.com/nicganon/p/3741609.html

muduo库源码剖析(一) reactor模式的更多相关文章

  1. muduo库源码剖析(二) 服务端

    一. TcpServer类: 管理所有的TCP客户连接,TcpServer供用户直接使用,生命期由用户直接控制.用户只需设置好相应的回调函数(如消息处理messageCallback)然后TcpSer ...

  2. caffe库源码剖析——net层

    net层的功能实现主要涉及到net.hpp和net.cpp文件,让我们要捋顺它是干了什么,是如何实现的. 1. net层使用到的参数 第一步要做的事,就是查看caffe.proto文件,弄清楚net都 ...

  3. ucore 源码剖析

    lab1 源码剖析 从实模式到保护模式 初始化ds,es和ss等段寄存器为0 使能A20门,其中seta20.1写数据到0x64端口,表示要写数据给8042芯片的Output Port;seta20. ...

  4. Golang 源码剖析:log 标准库

    Golang 源码剖析:log 标准库 原文地址:Golang 源码剖析:log 标准库 日志 输出 2018/09/28 20:03:08 EDDYCJY Blog... 构成 [日期]<空格 ...

  5. 设计模式(二十二)——状态模式(APP抽奖活动+借贷平台源码剖析)

    24.1 APP 抽奖活动问题 请编写程序完成 APP 抽奖活动 具体要求如下: 1) 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10% 2) 奖品数量固定,抽完就不能抽奖 3) 活动 ...

  6. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

  7. DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续)

    转载:http://blog.csdn.net/zssureqh/article/details/39237649 背景: 上一篇博文中,在对storescp工具源文件storescp.cc和DcmS ...

  8. DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求

    转载:http://blog.csdn.net/zssureqh/article/details/39213817 背景: 上一篇专栏博文中针对PACS终端(或设备终端,如CT设备)与RIS系统之间w ...

  9. 《Apache Spark源码剖析》

    Spark Contributor,Databricks工程师连城,华为大数据平台开发部部长陈亮,网易杭州研究院副院长汪源,TalkingData首席数据科学家张夏天联袂力荐1.本书全面.系统地介绍了 ...

随机推荐

  1. kali中的APT软件包处理工具(apt-get)、Debian软件包管理器(dpkg)、源代码压缩和Nessus安装实用指南

    写在前面的话 能看懂此博客的朋友,深信你有一定的Kali基础了. 使用APT软件包处理工具(apt-get).Debian软件包管理器(dpkg)来维护.升级和安装自定义及第三方应用程序 APT软件包 ...

  2. scala学习笔记2:面向对象编程部分基础

    以下主要记录的是看完scala in programming这本书Functional Objects(第六章)后的要点总结. 1,程序中可变对象(var)和不可变对象(val)使用的权衡问题 不可变 ...

  3. [转].net cookie版购物车

    本文转自:http://www.sulong.cc/article/program/aspx/110613114249.html #region 添加到购物车AddShoppingCar /// &l ...

  4. C#基础 集合

    //数组定义的时候 //需要定义数据类型 //需要定义初始长度 //int [] array = new int[5]; //int a = array.Length; //集合 //ArrayLis ...

  5. md5加密、Des加密对称可逆加密、RSA非对称可逆加密、https单边验证、银行U盾双边认证

    1.md5不可逆的加密方式,加密成一个32位的字符串.算法是公开的,任何语言的加密结果都是一样的.总有可能是重复的.     用途:             (1)防止明文存储:可以用作密码加密    ...

  6. C#知识点-枚举器和迭代器

    一.几个基本概念的理解 问题一:为什么数组可以使用foreach输出各元素 答:数组是可枚举类型,它实现了一个枚举器(enumerator)对象:枚举器知道各元素的次序并跟踪它们的位置,然后返回请求的 ...

  7. CF811C Vladik and Memorable Trip

    思路: 令dp[i]表示前i个的最大舒适度.则如果区间[j, i](1 < j <= i)满足条件,有如下转移:dp[i] = max(dp[i], dp[j - 1] + cur).其中 ...

  8. 多路开关模式的switch语句

    在实例10中,将break语句去掉之后,会将符合检验条件后的所有语句都输出.利用这个特点,可以设计多路开关模式的switch语句,例如:在平年一年12个月,1.3.5.7.8.10.12月是31天,4 ...

  9. Android基础TOP6_3:Gally和ImageSwitcher实现画廊

    结构: Activity: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ...

  10. NX自动出图 发布啦

    经过4个月的努力,终于面世啦!!!!1.安装文件 :http://yunpan.cn/Q49TWSJmy2i5Z    请下载后,按照“安装说明.txt ”进行安装!2.学习视频:http://yun ...