muduo源码分析之EventLoop事件循环

相关文件

muduo/net/EventLoop.h
muduo/net/EventLoop.cc

作用

EventLoop,顾名思义,事件循环。
创建了EventLoop对象的线程是IO线程,主要功能是运行事件循环EventLoop::loop()。
个人理解为调用poll/epoll的那个while循环。

使用

在loop方法中会调用poll/epoll监听事件,其中又涉及到Poller类和Channel类。

#include <muduo/net/EventLoop.h>
#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

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

	EventLoop loop;

	loop.loop();
	
	return 0;
}

EventLoop源码分析

本节仅从one loop per thread的角度小窥一下EventLoop的源码,涉及到Poller部分在Reactor小节在深入理解。

one loop per thread即一个线程只能有一个EventLoop对象。
EventLoop对象在构造时,会检查当前线程是否创建了其他EventLoop对象,如果已创建就终止程序(LOG_FATAL)。

构造函数

__thread EventLoop* t_loopInThisThread = 0;  //__thread表示是线程自己占有的变量,指向当前线程创建的Eventloop对象

EventLoop::EventLoop()
  : looping_(false),
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    iteration_(0),
    threadId_(CurrentThread::tid()),  //当前线程的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(
      std::bind(&EventLoop::handleRead, this));
  // we are always reading the wakeupfd
  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;  //在析构函数中将其置空
}

负面测试

在主线程创建了EventLoop对象,试图在另一个线程调用其loop方法,导致程序异常终止

#include <muduo/net/EventLoop.h>

#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

muduo::EventLoop* g_loop;

void threadFunc()
{

	g_loop.loop();
}

int main(void)
{

	EventLoop loop;
	g_loop = &loop;
	Thread t(threadFunc); //创建线程
	t.start();

	t.join();
	return 0;
}

loop()干了什么

作为EventLoop的主要方法,这里不深入看Poller和Chanel的源码,先留个大致的印象。

void EventLoop::loop()
{
  assert(!looping_);
  assertInLoopThread();  //检查是否在创建EventLoop的线程中调用
  looping_ = true;       //开始循环
  quit_ = false;  	 // 退出标志
  LOG_TRACE << "EventLoop " << this << " start looping";

  while (!quit_)
  {
    activeChannels_.clear(); //vector :活跃的Cannel
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);//poll
    ++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)  //开发时写日志
    {
      printActiveChannels();
    }
    // TODO sort channel by priority
    eventHandling_ = true;  //处理poll得到的事件

    //这里的一个cannel可理解为一个文件描述符和如何处理它的事件的回调函数的封装
    for (Channel* channel : activeChannels_)
    {
      currentActiveChannel_ = channel;
      currentActiveChannel_->handleEvent(pollReturnTime_);//处理fd的事件
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors();
  }

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

posted @ 2021-05-16 10:20  零十  阅读(179)  评论(0编辑  收藏  举报