muduo_net代码剖析之EventLoop

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84402031

TCP网络编程本质

TCP网络编程最本质的是处理三个半事件

  1. 连接建立:服务器accept(被动)接收连接,客户端connect(主动)发起连接
  2. 连接断开:主动断开(close、shutdown),被动断开(read返回0)
  3. 消息到达:文件描述符可读
  4. 消息发送完毕:这算半个。对于低流量的服务,可不必关心这个事件;这里的发送完毕是指数据写入操作系统缓冲区,将由TCP协议栈负责数据的发送与重传,不代表对方已经接收到数据

在这里插入图片描述

EventLoop是整个Reactor的核心。其类图如下:
在这里插入图片描述
one loop per thread意味着每个线程只能有一个EventLoop对象,用变量

__thread EventLoop* t_loopInThisThread = 0;

表示,在创建EventLoop对象时将t_loopInThisThread赋值,以后再创建时就可以检查这个变量,如果已经赋值就说明当前线程已经创建过EventLoop对象了。线程调用静态函数EventLoop::getEventLoopOfCurrentThread就可以获得当前线程的EventLoop对象的指针了。

对EventLoop的个人理解

  1. 与libevent的event_base类似,EventLoop也是一个事件循环,即EventLoop本质上是一个while循环,在while循环中调用了epoll_wait/poll监听注册在EventLoop上的事件/Channel
  2. EventLoop的使用与libevent的event_base一样,就是调用loop()函数对安插在EventLoop对象上的事件进行监听

EventLoop::loop()的时序图

在这里插入图片描述

  1. loop.loop()实际上是在while循环中调用了poll或epoll的poll函数,程序在此处阻塞,直到有事件被触发
  2. 当监听到事件被触发后,调用handleEvent()函数去处理事件
//事件循环,该函数不能跨线程调用,只能在创建该对象的线程中调用
void EventLoop::loop()
{
  assert(!looping_); 
  assertInLoopThread(); //断言当前处于创建该对象的线程中
  looping_ = true;
  quit_ = false;  // FIXME: what if someone calls quit() before loop() ?
  LOG_TRACE << "EventLoop " << this << " start looping";

  while (!quit_)
  {
	//清除活动通道
    activeChannels_.clear();
	
	//调用poll()[ 本质上epoll的epoll_wait、poll的poll() ],阻塞在这里
	//poll返回后,activeChannels_活动通道被填充
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
    ++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }
    eventHandling_ = true;
	//遍历每一个活动通道,并处理事件handleEvent
    for (Channel* channel : activeChannels_)
    {
      currentActiveChannel_ = channel;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors(); //执行pendingFunctors_中的任务
  }

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

示例代码

父子线程都创建了自己的loop,并且监控安插在各自身上的事件

#include <muduo/net/EventLoop.h>
#include <muduo/base/Thread.h>

#include <assert.h>
#include <stdio.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

void callback()
{
  printf("callback(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
  EventLoop anotherLoop;
}

void threadFunc()
{
  printf("threadFunc(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());

  assert(EventLoop::getEventLoopOfCurrentThread() == NULL);
  EventLoop loop; //子线程中的loop
  assert(EventLoop::getEventLoopOfCurrentThread() == &loop);
  loop.runAfter(1.0, callback); //1s后,调用callback
  loop.loop();
}

int main()
{
  printf("main(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());

  assert(EventLoop::getEventLoopOfCurrentThread() == NULL);
  EventLoop loop; //主线程中的loop
  assert(EventLoop::getEventLoopOfCurrentThread() == &loop);

  Thread thread(threadFunc);
  thread.start(); //启动子线程

  loop.loop(); //主线程中的loop调用loop()
}

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/84402031
今日推荐