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执行一些任务回调呢。
作者:cnblogs NicGanon</