WebServer----EventLoop

EventLoop类介绍

EventLoop是一个事件循环,遵循one-thread-one-loop,用于运行和管理epoll。只要该loop启动后,将一直循环这样一个事件循环。

数据成员:

bool looping_;    //用于标志该loop是否运行起来
//epoll结构,事件循环的核心部分。其生命周期由EventLoop控制
shared_ptr<Epoll> poller_; 
//唤醒通道,用于线程间的通信,主要是通知唤醒一个阻塞于epoll_wait中的Eventloop
int wakupfd_;    
bool quit_;    //标志退出状态
bool eventHandling;   //标志是否处于回调各个有事件产生的通道的回调函数过程中
mutable Mutexlock mutex_; //锁,跨线程注册Channel的时候需要加锁
std::vector<Funtor> pendingFunctors_;
bool callingPendingFunctors_;
const pid_t threadId_;
shared_ptr<Channel> pwakeupChannel_;  //通知通道,用于MainReactor唤醒SubReactor

EventLoop对象的构造

主要是创建事件分发器Epoll和唤醒通道

EventLoop::EventLoop()
:   looping_(false),
    poller_(new Epoll()),
    wakeupFd_(createEventfd()),
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    threadId_(CurrentThread::tid()),
    pwakeupChannel_(new Channel(this, wakeupFd_))
{
    if (t_loopInThisThread)
    {
        //LOG << "Another EventLoop " << t_loopInThisThread << " exists in this thread " << threadId_;
    }
    else
    {
        t_loopInThisThread = this;
    }
    //pwakeupChannel_->setEvents(EPOLLIN | EPOLLET | EPOLLONESHOT);
    pwakeupChannel_->setEvents(EPOLLIN | EPOLLET);
    pwakeupChannel_->setReadHandler(bind(&EventLoop::handleRead, this));   
    pwakeupChannel_->setConnHandler(bind(&EventLoop::handleConn, this));
    poller_->epoll_add(pwakeupChannel_, 0);
}

主要结构–loop循环

void EventLoop::loop()
{
    assert(!looping_);
    assert(isInLoopThread());
    looping_ = true;
    quit_ = false;
    //LOG_TRACE << "EventLoop " << this << " start looping";
    std::vector<SP_Channel> ret;
    while (!quit_)
    {
        //cout << "doing" << endl;
        ret.clear();
        //poll_wait和getEventRequest函数返回
        ret = poller_->poll();
        //调用每一个活跃Channel中相应事件的回调函数,完成事件处理
        eventHandling_ = true;
        for (auto &it : ret)
            it->handleEvents();
        eventHandling_ = false;
        //执行其他线程调用queueinloop添加进去的函数队列
        doPendingFunctors();   
        //调用定时器处理超期连接
        poller_->handleExpired();
    }
    looping_ = false;
}

RunInLoop和QueueInLoop跨线程精髓

/*如果在当前线程中调用本线程Eventloop对象的runInLoop,那么直接执行回调函数.
如果该EventLoop不在当前线程中,那么将调用queueInloop把该回调函数放到待执行
回调函数队列的末尾,并通过写eventfd唤醒该EventLoop线程。
*/
void EventLoop::runInLoop(Functor&& cb)
{
    if (isInLoopThread())
        cb();
    else
        queueInLoop(std::move(cb));
}

/*本线程也可以调用queueInloop将cb放到待执行函数队列中,如果该EventLoop
正处于loop函数中的执行callingPendingFunctors阶段,就需要写eventfd,否则
如果下一次一直没有事件产生,可能会影响下一次doPendingFunctors()的执行时间,
导致新添加的回调函数的执行为无限期拖后*/
void EventLoop::queueInLoop(Functor&& cb)
{
    {
        MutexLockGuard lock(mutex_);
        pendingFunctors_.emplace_back(std::move(cb));
    }
    if (!isInLoopThread() || callingPendingFunctors_)
        wakeup();
}

移动语义和右值引用可以减少拷贝,节约开销。

事件通知机制 eventfd

EventLoop的wakefd就是通过eventfd创建出来的

int createEventfd()
{
    int evtfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (evtfd < 0)
    {
        LOG << "Failed in eventfd";
        abort();
    }
    return evtfd;
}

类似的用于线程间通信还有sockpair和pipe,但是另外两个都需要两个描述符,而evenfd只需要一个描述符就可以,更节省开销。

eventfd这个文件描述符的上层持有者是eventloop,其回调函数有两个:

void EventLoop::handleRead()
{
    uint64_t one = 1;
    ssize_t n = readn(wakeupFd_, &one, sizeof one);
    if (n != sizeof one)
    {
        LOG << "EventLoop::handleRead() reads " << n << " bytes instead of 8";
    }
    //pwakeupChannel_->setEvents(EPOLLIN | EPOLLET | EPOLLONESHOT);
    //执行完读操作后重新注册读事件
    pwakeupChannel_->setEvents(EPOLLIN | EPOLLET);
}
//由Channel类的handleEvents函数可知,此函数在执行完读回调函数之后执行,更新注册pwakeupChannel事件
void EventLoop::handleConn()
{
    //poller_->epoll_mod(wakeupFd_, pwakeupChannel_, (EPOLLIN | EPOLLET | EPOLLONESHOT), 0);
    updatePoller(pwakeupChannel_, 0);
}

对epoll的一些操作

    void removeFromPoller(shared_ptr<Channel> channel)
    {
        //shutDownWR(channel->getFd());
        poller_->epoll_del(channel);
    }
    void updatePoller(shared_ptr<Channel> channel, int timeout = 0)
    {
        poller_->epoll_mod(channel, timeout);
    }
    void addToPoller(shared_ptr<Channel> channel, int timeout = 0)
    {
        poller_->epoll_add(channel, timeout);
    }

这些函数只会在EventLoop本线程内调用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值