muduo的reactor模式基本实现

这几天一直在看muduo的Eventloop(事件循环)这一块的源代码,感觉里面有好多东西例如:智能指针的使用,将eventfd,timerfd等linux新性能运用进去,C++一些容器的合理使用,还有如何能在多线程情况下减少锁的使用等都是我们应该学习的东西。

关于muduo实现的reactor模式,有三个关键的类

.事件分发器类Channel
.封装了I/O复用的Poller
.定时器接口类TimerQueue

接下来就给大家先一一介绍这几个类,然后才给大家介绍reactor的基本构成

1.事件分发类Channel

事件分发器Channel的数据成员如下

//定义事件类型变量
            static const int kNoneEvent;    //无事件
            static const int kReadEvent;    //可读事件
            static const int kWriteEvent;   //可写事件

            EventLoop *loop_;               //channel所属的loop
            const int fd_;                  //channel负责的文件描述符
            int events_;            //注册的事件
            int revents_;           //就绪的事件
            int index_;             //被poller使用的下标
            bool logHup_;           //是否生成某些日志

            boost::weak_ptr<void> tie_; //
            bool tied_;
            bool eventHandling_;
            bool addedToLoop_;
            ReadEventCallback readCallback_;    //读事件回调
            EventCallback writeCallback_;       //写事件回调
            EventCallback closeCallback_;       //关闭事件回调
            ReadEventCallback errorCallback_;   //错误事件回调

其中EventCallback和ReadEventCallback的声明如下

 typedef boost::function<void()> EventCallback;
            typedef boost::function<void(Timestamp)> ReadEventCallback;
        //处理事件
        void handleEvent(Timestamp receiveTime);

        //设置可读事件回调
        void setReadCallback(const ReadEventCallback &cb)
        {
            readCallback_ = cb;
        }

        //设置可写事件回调
        void setWriteCallback(const EventCallback &cb)
        {
            writeCallback_ = cb;
        }

        //设置关闭事件回调
        void setCloseCallback(const EventCallback &cb)
        {
            closeCallback_ = cb;
        }

        //设置错误事件回调
        void setErrorCallback(const EventCallback &cb)
        {
            errorCallback_ = cb;
        }

        void tie(const boost::shared_ptr<void>&);



        //返回注册的事件
        int events()const
        {
            return events_;
        }

        //设置注册事件
        void set_revents(int revt)
        {
            revents_ = revt;
        }

        //判断是否注册的事件
        bool isNoneEvent()const
        {
            return events_ == kNoneEvent;
        }

        //设置可读事件
        void enableReading()
        {
            events_ |= kReadEvent;
            update();
        }

        //销毁读事件
        void disableReading()
        {
            events_ &= ~kReadEvent;
            update();
        }

        //注册写事件
        void enableWriting()
        {
            events_ |= kWriteEvent;
         update();   
        }

        //销毁写事件
        void disableWriting()
        {
            events_ &= ~kWriteEvent;
            update();
        }

        //销毁所有事件
        void disableAll()
        {
            events_ = kNoneEvent;
            update();
        }

        //是否注册可写事件
        isWriting()const
        {
            return events_ & kWriteEvent;
        }
这里写代码片

Channel的主要功能为管理各种注册给poller的套接字描述符及其上发生的事件,以及事件发生了所调的回调函数

Channel的主要作用如下

1.首先我们给定Channel所属的loop以及其要处理的fd
2.接着我们开始注册fd上需要监听的事件,如果是常用事件(读写等)的话,我们可以直接调用接口enable***来注册对应fd上的事件,与之对应的是disable*用来销毁特定的事件
3.在然后我们通过set***Callback来事件发生时的回调

2.I/O复用类Poller

Poller类是个基类,它的定义如下

class Poller : boost::noncopyable
    {
        public:
            typedef std::vector<Channel *> ChannelList;

            Poller(EventLoop *loop);

            virtual ~Poller();

            //不许在I/O线程中调用,I/O复用的封装
            virtual Timestamp poll(int timeoutMs,ChannelList *activeChannels) = 0;

            //跟新Channel
            virtual void updateChannel(Channel *channel) = 0;

            //移除Channel
            virtual void removeChannel(Channel *channel) = 0;

            //这个channel是否在map中存在
            virtual bool hasChannel(Channel *channel)const;

            //默认poller方式
            static Poller *newDefaultPoller(EventLoop *loop);

            void assertInLoopThread()const
            {
                ownerLoop_->assertInLoopThread();
            }

        protected:
            typedef std::map<int,Channel*> ChannelMap;
            ChannelMap Channels_;           //存储事件分发器的map
        private:
            EventLoop *owerLoop_;           //属于哪个loop
    };

需要注意的是我们的事件分发器channel集用关联容器map来保存,map的关键字为channel所管理的fd,这样我们在更新已有的channel时时间复杂多会将为O(1)

接下来我们谈谈epoll对Poller的实现
EPollPoller类的定义为

class EPollPoller : public Poller
{
    public:
        EPollPoller(EventLoop *loop);
        virtual Timestamp poll(int timeoutMs,ChannelList *activeChannels);   //内部调用epoll_wait函数

        virtual void updateChannel(Channel *channel);

        virtual void removeChannel(Channel *Channel);       
    private:
        static const int kInitEventListSize = 16;           //epoll事件表的大小

        static const char *operatoionToString(int op);

        void fillActiveChannels(int numEvents,ChannelList *activeChannels)const; //将epoll返回的活跃事件填充到activeChannel

        void update(int operation,Channel *channel);    //对Channel的更改操作

        typedef std::vector<struct epoll_event> EventList;

        int epollfd_;       //epoll的事件表fd

        EventList events_;  //epoll事件数组
};

Poller类的主要功能如下

.调用poll函数监听注册了事件的文件描述符
.当poll返回时将发生事件的事件集装入channel中
.可以控制channel中事件的增删改

3.定时器TimerQueue

EventLoop直接调用的是定时器队列类TimerQueue类,该类的数据定义如下

typedef std::pair<Timestamp,Timer *> Entry;     //定时器入口
            typedef sta::set<Entry> TimerList;              //定时器集合
            typedef std::pair<Timer *,int64_t> ActiveTimer; //到时的定时器
            typedef std::set<ActiveTimeri> ActiveTimerSet;  //活跃的定时器集合

EventLoop *loop_;       //定时器所在的I/O循环
            const int timerfd_;     //定时器描述符
            Channel timerfdChannel_;

            TimerList timers_;      //定时器集合

            //for cancel()
            ActiveTimerSet activeTimers_;
            bool callingExpiredTimers_;
            ActiveTimerSet cancelingTimers_;

由上图我们发现我们用来存储定时器集的容器使用了set,set针对定时器有它天然的优势,首先set的特性是所存储的元素为默认升序的,这样当我们某个事件点取到期的定时器,就直接取该时间点之前的所有定时器就好,其次我们往set中添加定时器的效率也相对较好为0(logn)。但是用set有个问题,我们如何存储俩个定时时间一样的定时器呢?muduo的解决方案就是使用一个pair类型,pair为pair

增加一个定时器
 TimerId addTimer(const TimerCallback &cb,
                            Timestamp when,
                            double interval);

删除一个定时器
            void cancel(TimerId timerId);

上述接口函数,都不是线程安全的,但是muduo中并没有用加锁来解决问题,而是通过EventLoop中runInloop函数来使,上述函数在主I/O线程中执行,这样做的好处是,我们可以在上述函数实现中不使用锁,程序在运行过程中就会减少由于锁而引起的上下文切换,由于主I/O线程也不一定是一直忙,所以这种做法可能会在效率上有所提升

4.EventLoop类的实现

EventLoop类实现了reactor的基本模式
它的数据定义如下

typedef std::vector<Channel *> ChannelList; //事件分发器列表

            bool looping_;                  //是否在运行
            bool quit_;                     //是否退出事件循环
            bool eventHandling_;
            bool callingPendingFunctors_;
            int64_t iteration_;             //事件循环的次数
            const pid_t threadId_;          //运行loop的线程ID
            Timestamp pollReturnTime_;      //poll阻塞的时间
            boost::scoped_ptr<Poller> poller_;  //IO复用
            boost::scoped_ptr<TimerQueue> TimerQueue_;//定时器队列
            int wakeupFd_;                            //唤醒套接字
            boost::scoped_ptr<Channel> wakeupChannel_; //封装事件描述符 
            boost::any context_;

            ChannelList activeChannel_;             //以活跃的事件集
            Channel *currentActiveChannel_;         //当前处理的事件集

            MutexLock mutex_;                       //封装的互斥锁
            std::vector<Functor> pendingFunctors_;  //需要在主I/O线程执行的任务

EventLoop通过boost库下的智能指针scoped_ptr来管理Poller_,TimerQueue_,wakeupChannel_对象,这样不容易发生内存显露,其中变量pendingFunctors_为需要在I/O线程中执行的任务集,例如上面所讲的定时器的增删接口的执行,就会先放在此集合里,然后有主I/O线程来执行,那么主线程在调用loop函数之后会阻塞在poller函数中,此时我们应该如何唤醒I/O线程呢?muduo中采用了linux的新特性eventfd来唤醒I/O线程,他的具体用法在我的前几篇博客中有介绍

Eventloop的主要功能如下

.首先我们因该调用updateChannel来添加一些事件
.接着我们就可以调用loop函数来执行事件循环了,在执行事件循环的过程中,我们会阻塞在poller_poll调用处,当有事件发生时,Poller类就会把活跃的事件放在activeChannel集合中
.之后我们调用Channel中的handleEvent来处理事件发生时对应的回调函数,处理完事件函数后还会处理必须有I/O线程来完成的doPendingFuncors函数

当然我们可以在中间的过程中注册一些普通事件或通过run*类函数来注册定时事件,我们也可以调用updateChannel和removeChannel来增删该Channel

EventLoop的源码如下

EventLoop.h

#ifndef MUDUO_NET_EVENTLOOP_H
#define MUDUO_NET_EVENTLOOP_H

#include <vector>

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>

#include <muduo/base/Mutex.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Timestamp.h>
#include <muduo/net/Callbacks.h>
#include <muduo/net/TimerId.h>

namespace muduo
{
namespace net
{
    class Channel;
    class Poller;
    Class TimerQueue;

    class EventLoop : boost::noncopyable
    {
        public:
            typedef boost::function<void()> Functor;

            EventLoop();
            ~EventLoop();

            //主循环
            void loop();

            //退出主循环
            void quit();

            //poll延迟的时间
            Timestamp pollReturnTime()const
            {
                return pollReturnTime_;
            }

            //迭代次数
            int64_t iteration()const
            {
                return iteration_;
            }

            //在主循环中运行
            void runInLoop(const Functor &cb);

            //插入主循环任务队列
            void queueInLoop(const Functor &cb);

            //某个时间点执行定时回调
            TimerId runAt(const Timestamp &time,const TimerCallback &cb);

            //某个时间点之后执行定时回调
            TimerId runAfter(double delay,const TimerCallback & cb);

            //在每个时间间隔处理某个回调事件
            TimerId runEvery(double interval,const TimerCallback &cb);

            //删除某个定时器
            void cancel(TimerId timerId);

            //唤醒事件通知描述符
            void wakeup();

            //跟新某个事件分发器
            void updateChannel(Channel *channel);

            //移除某个事件分发器
            void removeChannel(Channel *channel);

            bool hasChannel(Channel *channel);

            //如果不在I/O线程中则退出程序
            void assertInLoopThread()
            {
                if(!isInLoopThread())
                {
                    abortNotInLoopThread();
                }
            }

           // 检测是否在I/O线程中
            bool isInLoopThread()const
            {
                return threadId_ == CurrentThread::tid();
            }

            //是否正在处理事件
            bool eventHandling()const { return eventHandling_;}

            void setContext(const boost::any &contex)
            {
                return context_;
            }

            const boost::any &getContext()const
            {
                return context_;
            }

            boost::any *getMutableContext()
            {
                return &context_;
            }

            //判断当前线程是否为I/O线程
            static EventLoop *getEventLoopOfCurrentThread();

        private:
            //不在主I/O线程
            void abortNotInLoopThread();

            //将事件通知描述符里的内容读走,以便让其继续检测事件通知
            void handleRead();

            //执行转交给I/O的任务
            void doPendingFunctors();

            //将发生的事件写入日志
            void printActiveChannels()const;

            typedef std::vector<Channel *> ChannelList; //事件分发器列表

            bool looping_;                  //是否在运行
            bool quit_;                     //是否退出事件循环
            bool eventHandling_;
            bool callingPendingFunctors_;
            int64_t iteration_;             //事件循环的次数
            const pid_t threadId_;          //运行loop的线程ID
            Timestamp pollReturnTime_;      //poll阻塞的时间
            boost::scoped_ptr<Poller> poller_;  //IO复用
            boost::scoped_ptr<TimerQueue> TimerQueue_;//定时器队列
            int wakeupFd_;                            //唤醒套接字
            boost::scoped_ptr<Channel> wakeupChannel_; //封装事件描述符 
            boost::any context_;

            ChannelList activeChannel_;             //以活跃的事件集
            Channel *currentActiveChannel_;         //当前处理的事件集

            MutexLock mutex_;                       //封装的互斥锁
            std::vector<Functor> pendingFunctors_;  //需要在主I/O线程执行的任务

    };
}
}
#endif MUDUO_NET_ENENTLOOP_H

EventLoop.cc

#include <muduo/net/EventLoop.h>

#include <muduo/base/Logging.h>
#include <muduo/base/Mutex.h>
#include <muduo/net/Channel.h>
#include <muduo/net/Poller.h>
#include <muduo/net/SocketsOps.h>
#include <muduo/net/TimerQueue.h>

#include <boost/bind.hpp>

#include <signal.h>
#include <sys/eventfd.h>

using namespace muduo;
using namespace muduo::net;

namespace
{
    __thread EventLoop *t_loopInThisThread = 0;

    const int kPollTimeMs = 10000;

    int createEventfd()
    {
        int evtfd = ::eventfd(0,EFD_NONBLOCK | EFD_CFD_CLOEXEC);

        if(evtfd < 0)
        {
            LOG_SYSERR << "Failed in eventfd";
            abort();
        }
        return evtfd;
    }
}


EventLoop *EventLoop::getEventLoopOfCurrentThread()
{
    return t_loopInThisThread;
}

EventLoop::EventLoop()
    :looping_(false),
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    iteration_(0),
    threadId_(CurrentThread::tid()),
    poller_(Poller::newDefaultPoller(this)),
    timerQueue_(new TimerQueue(this)),
    wakeupFd_(createEventfd()),
    wakeupChannel_(new Channel(this,wakeupFd_)),
    currentActiveChannel_(NULL)
{
    LOG_DEBUG << "EventLoop created " << this << "in thread" << threadId_;
    if(t_loopInThisThread)
    {
        LOG_FATAL << "Another EventLoop " << t_loopInThisThread << "exists in this thread" << threadId_;
    }

    else
    {
        t_loopInThisThread = this;
    }

    //注册读完成时的回调函数
    wakeupChannel_->setReadCallback(boost::bind(&EventLoop::handleRead,this));

    //注册可读事件
    wakeupChannel_->enableReading();
}


EventLoop::~EventLoop()
{
    LOG_DEBUG << "EventLoop" <<this<<"of thread" << threadId_
              <<"destructs in thread" << CurrentThread::tid();
    wakeupChannel_->disableAll();
    wakeupChannel_->remove();
    ::close(wakeupFd_);
    t_loopInThisThread = NULL;
}

void EventLoop::loop()
{
    assert(!looping_);
    assertInLoopThread();
    looping_ = true;
    quit_ = false;

    LOG_TRACE << "EventLoop" << this << "start looping";

    while(!quit_)
    {
        activeChannels_.clear();
        pollReturnTime_ = poller_->poll(kPollTimeMs,&activeChannels_);
        //计算执行多少次I/O返回
        if(Logger::LogLevel() <= Logger::TRACE)
        {
            printActiveChannels();
        }

        eventHandling_ = true;

        //处理就绪事件
        for(ChannelList::iterator it = activeChannels_.begin();
                it != activeChannels_.end(); ++it)
        {
            currentActiveChannel_ = *it;
            currentActiveChannel_->handleEvent(pollReturnTime_);
        }

        currentActiveChannel_ = NULL;
        eventHandling_ = false;
        //处理一些其他任务
        doPendingFunctors();
    }

    LOG_TRACE << "EventLoop" << this << "stop looping";
    looping_ = false;
}

void EventLoop::quit()
{
    quit_ = true;

    if(!isInLoopThread())
    {
        wakeup();
    }
}

void EventLoop::runInLoop(const Functor &cb)
{
    if(isInLoopThread())
    {
        cb();
    }
    else
    {
        queueInLoop(cb);
    }
}


void EventLoop::queueInLoop(const Functor &cb)
{
    {
        MutexLockGuard lock(mutex_);
        pendingFunctors_.push_back(cb);
    }

    if(!isInLoopThread() || callingPendingFunctors_)
    {
        wakeup();
    }
}


TimerId EventLoop::runAt(const Timestamp &time,const TimerCallback &cb)
{
    return timerQueue_->addTimer(cb,time,0.0);
}

TimerId EventLoop::runAfter(double delay,const TimerCallback &cb)
{
    Timestamp time(addTime(Timestamp::now(),delay));
    return runAt(time,cb);
}

TimerId EventLoop::runEvery(double interval,const TimerCallback &cb)
{
    Timestamp time(addTime(Timestamp::now()),interval);
    return timerQueue_->addTimer(cb,time,interval);
}


void  EventLoop::cancel(TimerId timerId)
{
    return timerQueue_->cancel(timerId);
}


void EventLoop::updateChannel(Channel *channel)
{
    assert(channel->ownerLoop() == this);
    assertInLoopThread();
    poller_->updateChannel(channel);
}


void EventLoop::removeChannel(Channel *channel)
{
    assert(channel->ownerLoop() == this);
    assertInLoopThread();
    if(eventHandling_)
    {
        assert(currentActiveChannel_ == channel ||
                std::find(activeChannels_.begin(),activeChannels_.end(),channel) == activeChannels_.end());
    }

    poller_->removeChannel(channel);
}

bool EventLoop::hasChannel(Channel *channel)
{
    assert(channel->ownerLoop() == this);
    assertInLoopThread();
    return poller_->hasChannel(channel);
}

void EventLoop::abortNotInLoopThread()
{
    LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop" << this 
              << "was created in threadId_ = " << threadId_
              << ",current thread id = " << CurrentThread::tid();
}

void EventLoop::wakeup()
{
    uint64_t one = 1;
    ssize_t n = sockets::write(wakeupFd_,&one,sizeof one);
    if(n != sizeof one)
    {
        LOG_ERROR << "EventLoop::wakeup() writes" << n << "bytes instead of 8";
    }
}

void EventLoop::handleRead()
{
    uint64_t one = 1;
    ssize_t n = sockets::read(wakeupFd_,&one,sizeof one);
    if(n != sizeof one)
    {
        LOG_ERROR << "EventLoop::handleRead() reads" << n << "bytes instead of 8";
    }
}

void EventLoop::doPendingFunctors()
{
    std::vector<Functor> Functors;
    callingPendingFunctors_ = true;

    {
        MutexLockGuard lock(mutex_);
        Functors.swap(pendingFunctors_);    //提高效率且防止死锁
    }

    for(ssize_t i = 0;i < Functors.size();++i)
    {
        Functors[i]();
    }
    callingPendingFunctors_ = false;
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值