muduo库源码剖析(一) reactor模式
一. Reactor模式简介
Reactor释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。
二. moduo库Reactor模式的实现
muduo主要通过3个类来实现Reactor模式:EventLoop,Channel,Poller。
1. EventLoop
事件循环。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
事件分发器。每个Channel只属于一个EventLoop,每个Channel只负责一个文件描述符fd的IO事件分发,但其不拥有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
Poller是IO multiplexing的封装,封装了poll和epoll。Poller是EventLoop的间接成员,只供拥有该Poller的EventLoop在IO线程调用。生命期与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模式的更多相关文章
- muduo库源码剖析(二) 服务端
一. TcpServer类: 管理所有的TCP客户连接,TcpServer供用户直接使用,生命期由用户直接控制.用户只需设置好相应的回调函数(如消息处理messageCallback)然后TcpSer ...
- caffe库源码剖析——net层
net层的功能实现主要涉及到net.hpp和net.cpp文件,让我们要捋顺它是干了什么,是如何实现的. 1. net层使用到的参数 第一步要做的事,就是查看caffe.proto文件,弄清楚net都 ...
- ucore 源码剖析
lab1 源码剖析 从实模式到保护模式 初始化ds,es和ss等段寄存器为0 使能A20门,其中seta20.1写数据到0x64端口,表示要写数据给8042芯片的Output Port;seta20. ...
- Golang 源码剖析:log 标准库
Golang 源码剖析:log 标准库 原文地址:Golang 源码剖析:log 标准库 日志 输出 2018/09/28 20:03:08 EDDYCJY Blog... 构成 [日期]<空格 ...
- 设计模式(二十二)——状态模式(APP抽奖活动+借贷平台源码剖析)
24.1 APP 抽奖活动问题 请编写程序完成 APP 抽奖活动 具体要求如下: 1) 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10% 2) 奖品数量固定,抽完就不能抽奖 3) 活动 ...
- Apache Spark源码剖析
Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著 ISBN 978-7-121-25420- ...
- DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续)
转载:http://blog.csdn.net/zssureqh/article/details/39237649 背景: 上一篇博文中,在对storescp工具源文件storescp.cc和DcmS ...
- DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求
转载:http://blog.csdn.net/zssureqh/article/details/39213817 背景: 上一篇专栏博文中针对PACS终端(或设备终端,如CT设备)与RIS系统之间w ...
- 《Apache Spark源码剖析》
Spark Contributor,Databricks工程师连城,华为大数据平台开发部部长陈亮,网易杭州研究院副院长汪源,TalkingData首席数据科学家张夏天联袂力荐1.本书全面.系统地介绍了 ...
随机推荐
- C# 传值和传引用 ( ref out in )
引用类型的变量不直接包含其数据:它包含的是对其数据的引用.当通过值传递引用类型的参数时,有可能更改引用所指向的数据,如某类成员的值(更改属性的值),但是无法更改引用本身的值:也就是说,不能使用相同的引 ...
- 282 Expression Add Operators 给表达式添加运算符
给定一个仅包含0-9的字符串和一个目标值,返回在数字之间添加了二元运算符(不是一元的) +.-或*之后所有能得到目标值的情况.例如:"123", 6 -> ["1+ ...
- Cesium加载影像
注意:使用自定义数据源时,Cesium.Viewer类参数必须设置为 baseLayerPicker:false A. 使用天地图数据源 //天地图var provider=new Cesium.We ...
- LN : leetcode 242 Valid Anagram
lc 242 Valid Anagram 242 Valid Anagram Given two strings s and t, write a function to determine if t ...
- SAS进阶《深入分析SAS》之数据汇总和展现
SAS进阶<深入分析SAS>之数据汇总和展现 1. 通过Print过程制作报表 proc print <data=数据集>; run; 选项: obs=修改观测序号列标签 no ...
- IDEA 导入maven项目,显示:nothing to show
问题描述: IDEA 导入maven项目,import project,默认下一步,直到Select Maven projects to import界面无选项,提示:nothing to show ...
- Python :用两个栈实现队列
转自:http://blog.csdn.net/Lynette_bb/article/details/75092745 牛客网上的剑指 offer的在线编程: 题目描述 用两个栈来实现一个队列,完成队 ...
- CAD得到所有实体方法(网页版)
主要用到函数说明: IMxDrawSelectionSet::AllSelect 得到当前空间的所有实体.详细说明如下: 参数 说明 [in,defaultvalue(NULL)] IMxDrawRe ...
- SSH命令行传输文件到远程服务器
Ubuntu操作系统 SCP命令 使用方式如下: 1.上传本地文件到远程服务器 scp /var/www/test.php root@192.168.0.101:/var/www/ 把本机/var/w ...
- Linux(Centos7)下搭建SVN服务器(新手上路)
以前都是别人直接给地址在svn上,下载或者上传东西,如今要自己建一个版本库用来存放东西.1.安装svnyum install -y subversion 2.查看svn安装位置还有哪些文件rpm -q ...