zoukankan      html  css  js  c++  java
  • muduo源代码分析--Reactor模式在muduo中的使用

    一. 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()。通过此调用获得一个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

    事件分发器。每一个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 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运行一些任务回调呢。

  • 相关阅读:
    在windows下安装mongodb(1)
    kettle过滤记录运用
    Robberies(简单的01背包 HDU2955)
    Alice and Bob(贪心HDU 4268)
    A Simple Problem with Integers(树状数组HDU4267)
    A Round Peg in a Ground Hole(凸包应用POJ 1584)
    Fishnet(暴力POJ 1408)
    Wall(凸包POJ 1113)
    Pipe(点积叉积的应用POJ1039)
    I love sneakers!(分组背包HDU3033)
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6846830.html
Copyright © 2011-2022 走看看