muduo源码分析--Reactor模式在muduo中的使用
一. Reactor模式简介
二. moduo库Reactor模式的实现muduo主要通过3个类来实现Reactor模式:EventLoop,Channel,Poller。 1. 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(),通过此调用获得一个vectoractiveChannels_的就绪事件集合,再遍历该容器,执行每个Channel的Channel::handleEvent()完成相应就绪事件回调,最后执行pendingFunctors_排队的函数。上述一次循环就是一次Reactor模式完成。 runInLoop(boost::function),实现用户指定任务回调,若是EventLoop隶属的线程调用EventLoop::runInLoop()则EventLoop马上执行;若是其它线程调用则执行EventLoop::queueInLoop(boost::function将任务添加到队列中(线程转移)。EventLoop如何获得有任务这一事实呢?通过eventfd可以实现线程间通信,具体做法是:其它线程向EventLoop::vector >添加任务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
数据成员: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
数据成员:vector pollfds_事件结构体数组用于poll的第一个参数; map channels_用于文件描述符fd到Channel的映射便于快速查找到相应的Channel 主要功能函数:updateChannel(Channel*) 用于将传入的Channel关心的事件注册给Poller。 poll(int timeoutMs,vector 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执行一些任务回调呢。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |