当前位置:网站首页>【MUDUO】Poller抽象类

【MUDUO】Poller抽象类

2022-07-23 06:27:00 Cx330( ͡ _ ͡°)

TcpServer上有我们一个重要的组件,就是事件循环(EventLoop),一个线程对应着一个事件循环,一个EventLoop里边对应着就是一个poller 和一堆的channel,channel上一篇实现完了,最重要的就是这儿的poller了。

Poller为甚么实现成抽象类?因为EventLoop不可能直接的去调用Epoll或者Poll,它是从抽象层面直接使用了这个抽象类poller,到时候引用不同的派生类对象调用它的同名覆盖方法。就可以非常方便的去扩展不同的IO复用能力了。就是多路分发器.

Poller.h

#pragma once
#include "Timestamp.h"
#include "noncopyable.h"
#include <vector>
#include <unordered_map>

//只要是只是指针声明,就只做前置声明就可以
class Channel;
class EventLoop;

//muduo库中多路事件分发器的核心IO复用模块
class Poller: noncopyable
{
    public:
        using ChannelList = std::vector<Channel*>;

        Poller(EventLoop *loop);

        virtual ~Poller() = default;

        //对epoll来说相当于epoll_wait
        //当前激活的Channel,或者说是对某些事件感兴趣的channel,需要poller去照顾的channel
        //给所有IO复用保留统一的接口,让派生类去重写
        virtual Timestamp poll(int timeoutMs, ChannelList *activeChannels) = 0;
        
        //对epoll来说相当于epoll_ctl
        virtual void updateChannel(Channel *channel) = 0;
        //对epoll来说相当于epoll_del
        virtual void removeChannel(Channel *channel) = 0;

        //判断参数channel是否在当前的poller当中
        bool hasChannel(Channel *channel) const;

        //获取到这个事件循环的一个poller,这就像我们单例模式中的get instance一样,获取一个实例
        //EventLoop可以通过这个接口获取到默认的IO复用的具体实现
        static Poller* newDefaultPoller(EventLoop *loop);

    private:
        EventLoop *ownerLoop_; //定义poller所属的事件循环EventLoop
    protected:
        //map的ky:sockfd  value:sockfd所属的channel通道类型
        using ChannelMap =  std::unordered_map<int, Channel*>;
        ChannelMap channels_;

};




Poller.cc

#include "Channel.h"
#include "EventLoop.h"
#include "Logger.h"

#include <sys/epoll.h>

const int Channel::kNoneEvent = 0; 
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI;
const int Channel::kWriteEvent = EPOLLOUT;


Channel::Channel(EventLoop *loop, int fd)
    :loop_(loop),fd_(fd),events_(0),revents_(0),index_(-1),tied_(false)
{}


Channel::~Channel(){}

//channel里的tie方法什么时候被调用过? 后边再看
void Channel::tie(const std::shared_ptr<void> &obj)
{
    tie_ = obj; //绑定成弱智能指针观察强智能指针
    tied_ = true;//绑定过了就置成true
}

/**
 * @brief 当改变channel所表示fd的events事件后,update负责在poller里面更改fd相应的时间epoll_ctl
 * 
 * poller在哪呢? poller 跟channel是两个不同的模块。因为epoll不属于channel管,但是他们两都是数据EventLoop的
 * 
 * EventLoop 包含了ChannelList"也就是一个channel列表", 和一个poller
 * 
 * 所以channel本身想向Channel注册fd对应的事件是做不了的,因为它又没有poller对象,所以需要通过EventLoop来做这件事情
 * 
 */
void Channel::update()
{
    //通过Channel所属的EventLoop,调用poller的相应方法,注册fd的Events事件
    //add code
    //loop_->updateChannel(this);
}

//在channel所属的EventLoop中的容器,把自己给删除掉
void Channel::remove()
{
    //add code
    // loop_->removeChannel(this);
}


//fd得到poller通知以后,处理事件的
void Channel::handleEvent(Timestamp receiveTime)
{
    if(tied_)
    {
        std::shared_ptr<void> guard = tie_.lock();
        if(guard)
        {
            handleEventWithhGuard(receiveTime);
        }
    }
    else
    {
        handleEventWithhGuard(receiveTime);
    }
}

//根据poller通知的channel发生的具体事件,由channel负责调用具体的回调操作
void Channel::handleEventWithhGuard(Timestamp receiveTime)
{
    LOG_INFO("channel handleEvent revent:%d\n", revents_);

    if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN))
    {
        if(closeCallback_)
        {
            closeCallback_();
        }
    }
    //事件发生错误
    if(revents_ & EPOLLERR)
    {
        if(errorCallback_)
        {
            errorCallback_();
        }
    }
    //可读事件
    if(revents_ & (EPOLLIN | EPOLLPRI))
    {
        if(readCallback_)
        {
            readCallback_(receiveTime);
        }
    }

    if(revents_ & EPOLLOUT)
    {
        if(writeCallback_)
        {
            writeCallback_();
        }
    }

}

Poller类中的静态方法:

static Poller* newDefaultPoller(EventLoop *loop);

为什么不直接实现在Poller.cc文件中?

1.如果你真的把它实现在Poller.cc中,从语法上来说没有任何问题,为什么,因为你这个静态方是在这个类里边定义的,那理所当然的就应该在这个cc文件中去定义实现。但是你要注意:这个可是要生成一个具体的一个IO复用对象,并返回一个基类的指针,所以你在这里是不是一定要包含了派生类的头文件。你才能在这去创建一个poller的实例化对象,那么请问这个合理吗?

Poller* newDefaultPoller(EventLoop *loop)
{
    return new EPollPoller();
}

肯定不合理的,因为在继承结构中poller属于上基类,派生类只能引用基类头文件,而基类不能引用派生类的问头件,所以你在这个抽象的基类poller中直接去引用派生类的的头文件,这是一个不好的实现。

我是基类,你们派生类包含我天经地义,我作为抽象,我能不能去include的你们的每个派生类?不现实

::getenv("key")  获取环境中的变量,类似json, 头文件#include <stdlib.h>

 我们需要新创建一个cc文件:DefaultPoller.cc

#include "Poller.h"


#include <stdlib.h>

Poller* Poller::newDefaultPoller(EventLoop *loop)
{
    if(::getenv("MUDUO_USE_POLL"))
    {
        return nullptr; //生成poll的实例
    }
    else
    {
        return nullptr; //生成epoll的实例
    }
}

原网站

版权声明
本文为[Cx330( ͡ _ ͡°)]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_40533189/article/details/125902329