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可以统一事件源的优势。

更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/cplus/

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执行一些任务回调呢。

作者:cnblogs NicGanon</

时间: 2024-09-13 12:43:38

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

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

一. TcpServer类: 管理所有的TCP客户连接,TcpServer供用户直接使用,生命期由用户直接控制.用户只需设置好相应的回调函数(如消息处理messageCallback)然后TcpServer::start()即可. 主要数据成员: boost::scoped_ptr<Accepter> acceptor_; 用来接受连接 std::map<string,TcpConnectionPtr> connections_; 用来存储所有连接 connectonCallbac

《GDAL源码剖析与开发指南》一一1.6 GDAL编译

1.6 GDAL编译 GDAL源码剖析与开发指南 GDAL的编译分为三个小节,第一节是常规编译,使用微软的Visual Studio 2008进行编译,如果用微软的其他编译器,编译过程与此类似.需要注意的是,GDAL已经不再支持Visual C++6.0编译器.第二小节主要说明怎样修改GDAL的配置文件,以使GDAL支持更多的数据格式以及算法等.第三小节对编译GDAL的64位版本进行说明. 1.6.1 常规编译 1.使用Visual Studio 2008 IDE编译 在GDAL的源代码目录中,

JS魔法堂:mmDeferred源码剖析

一.前言   avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见:mmDeferred@github   二.API说明    {Deferred} Deferred({Function|Object} mixin?) ,创建一个Deferred实例,当mixin的类型为Object时,将mixin的属性和函数附加到Deferred实例的Promise实例上

Mongoose源码剖析:Introduction and Installation

引言 要剖析Mongoose的源码,首先你得知道它的一些基本情况和特性.并去使用它.本文就是介绍Mongoose是个什么东西?及如何安装和使用?这里假设你知道什么web服务器软件.web服务器使用什么协议.怎么提供服务等,如果你还不知道建议首先阅读:<Mongoose源码剖析:外篇之web服务器>. 本文主要内容如下: 1.Mongoose介绍 2.Mongoose的编译和使用 3.Makefile的简单分析 4.如何将Mongoose嵌入到应用程序中 1.Mongoose介绍 Mongoos

Spring源码剖析——Bean的配置与启动

IOC介绍   相信大多数人在学习Spring时 IOC 和 Bean 算得上是最常听到的两个名词,IOC在学习Spring当中出现频率如此之高必然有其原因.如果我们做一个比喻的话,把Bean说成Spring中主角的话,那么IOC便是这个主角进行演出的舞台,没有IOC作为Bean的承载,那么Bean便不会在编程中大放异彩.作为Spring核心组件的重要一员,了解其内部实现对我们编程和窥探Spring内幕是相当有帮助的,下面一步步从源码的角度来剖析IOC究竟是怎样实现的. 接口设计   首先我们先

Mongoose源码剖析:核心处理模块

引言 前面我们介绍了 Mongoose所有的几个主要的数据结构mg_context.mg_connection.mg_request_info,还有Mongoose的 生命主线.有了这些基础就可以来看看Mongoose的核心处理工作是怎样的.如果你还没有阅读前面的文章,你可以通过下面的隧道直通: Mongoose源码剖析:外篇之web服务器 Mongoose源码剖析:Introduction and Installation Mongoose源码剖析:数据结构篇 Mongoose源码剖析:mon

《GDAL源码剖析与开发指南》一一1.8 GDAL帮助文档生成

1.8 GDAL帮助文档生成 GDAL源码剖析与开发指南1.8.1 Doxygen简介 Doxygen是生成C++注释文档的标准工具,也支持其他语言,如C.Objective-C.C#.PHP.Java.Python.IDL (Corba and Microsoft flavors).Fortran.VHDL.Tcl等. 我们可以通过三种方式来使用Doxygen. 1.从源文件中以HTML形式生成在线文档浏览器,或者以LATEX形式生成非在线的参考手册,也支持RTF (MS-Word).Post

《GDAL源码剖析与开发指南》一一1.5 GDAL源码目录

1.5 GDAL源码目录 GDAL源码剖析与开发指南 下载的GDAL源代码压缩包目录如图1-2所示,使用SVN迁出的GDAL源代码目录如图1-3所示. 从图1-2和图1-3所示可以看出,不管用什么方式获取GDAL的源代码,它的目录结构都是一样的.下面我们就针对目录结构中的每个文件夹和文件作一个简单的说明(按照字母顺序). 1.5.1 文件夹说明 对于图1-3中所示的文件夹,详细说明如下. alg:该文件夹存放的是GDAL库中提供的一些算法的源代码,这些算法包括但不限于:DEM生成等高线算法,图像

《GDAL源码剖析与开发指南》导读

前言 GDAL源码剖析与开发指南 GDAL全称是Geospatial Data Abstraction Library(地理空间数据抽象库),是一个在X/MIT许可协议下读写空间数据(包括栅格数据和矢量数据)的开源库,它利用抽象数据模型来表达所支持的各种文件格式,还使用一系列命令行工具来进行数据转换和处理. 由于GDAL库支持很多数据格式,目前几乎所有的GIS和RS软件底层都使用GDAL来读写空间数据. 目前国内外关于GDAL的图书非常少,比较权威的只有GDAL官网的说明文档,导致用户缺少实用的