负责处理事件循环的关键类,遵循one loop per thread原则,所以一个线程中只有一个EventLoop对象,通过loop进入事件循环(while循环),在循环中主要有四个作用,按顺序是处理定时器事件,IO多路复用检测,处理检测返回的活动通道事件,处理自己的额外消息事件。one loop per thread已经是几乎所有网络库通用技术,需要深刻理解。
成员变量:
private:
typedef std::vector<Channel*> ChannelList;
bool looping_;
bool quit_;
bool eventHandling_;
bool callingPendingFunctors_;
const std::thread::id threadId_;
Timestamp pollReturnTime_;
std::unique_ptr<Poller> poller_;
std::unique_ptr<TimerQueue> timerQueue_;
int64_t iteration_;
#ifdef _WIN64
SOCKET wakeupFdSend_;
SOCKET wakeupFdListen_;
SOCKET wakeupFdRecv_;
#else
SOCKET wakeupFd_;
#endif
std::unique_ptr<Channel> wakeupChannel_;
ChannelList activeChannels_;
Channel* currentActiveChannel_;
std::mutex mutex_;
std::vector<Functor> pendingFunctors_;
Functor frameFunctor_;
成员变量比较多,先简单讲下,下面将具体功能还会再讲一遍,looping是循环开启的标志位,quit是退出循环标志位,eventHandling_是正在处理IO事件标志位,callingPendingFunctors_是正在处理其他事件标志位,threadId是创建EventLoop的线程Id,pollReturnTime_是多路复用返回时间戳,poller_是封装IO多路复用的类,timerQueue是处理一些事件定时器队列,wakeup的三个socket是在windows下,该类给自己发送消息唤醒loop的Io复用,处理设置其他的事件,同样wakeupFd_是linux下的接口,wakeupChannel是处理自己事件的Channel,activeChannels是IO复用检测返回的活动通道集合,currentActiveChannel_是正在处理消息的Channel,pendingFunctors是自己设置的额外的处理函数集合。
重要函数及作用:
构造函数:
EventLoop::EventLoop()
:looping_(false),
quit_(false),
eventHandling_(false),
callingPendingFunctors_(false),
threadId_(std::this_thread::get_id()),
timerQueue_(new TimerQueue(this)),
iteration_(0L),
currentActiveChannel_(NULL)
{
createWakeupfd();
#ifdef _WIN64
wakeupChannel_.reset(new Channel(this, wakeupFdSend_)); //传入连接的socket
poller_.reset(new SelectPoller(this));//Select模式
#else
wakeupChannel_.reset(new Channel(this, wakeupFd_));
poller_.reset(new EPollPoller(this));
#endif
if (t_loopInThisThread)
{
LOGF("Another EventLoop exists in this thread ");
}
else
{
t_loopInThisThread = this;
}
wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));
// we are always reading the wakeupfd
wakeupChannel_->enableReading();
//std::stringstream ss;
//ss << "eventloop create threadid = " << threadId_;
//std::cout << ss.str() << std::endl;
}
EventLoop是在线程池分配的某一个线程中创建出来,不需要传入参数,在构造函数初始化列表中完成一些变量的初始化。我们可以看到函数体内主要的是创建了三个fd(linux只需要一个Fd)以及用一个活动通道注册他们,他们的作用我会在下面讲。我们还看到了一个plloer,这是封装了IO多路复用(selelct,poll,epoll)的类。
关键函数处理事件:
void EventLoop::loop()
{
//assert(!looping_);
//事件循环必须在io线程执行 如果不在代表发生错误,直接退出程序。
assertInLoopThread();
looping_ = true;
quit_ = false; // FIXME: what if someone calls quit() before loop() ?
LOGD("EventLoop 0x%x start looping", this);
while (!quit_)
{
//定时器检测
timerQueue_->doTimer();
activeChannels_.clear();
//调用poll返回活动的通道,有可能是唤醒返回的
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
//if (Logger::logLevel() <= Logger::TRACE)
//{
printActiveChannels(); //打印下来
//}
++iteration_;
// TODO sort channel by priority
eventHandling_ = true;
for (const auto& it : activeChannels_)
{
currentActiveChannel_ = it;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
currentActiveChannel_ = nullptr;
eventHandling_ = false;
//这种设计使得IO线程也能执行一些计算任务,避免了IO线程在不忙时长期阻塞在IO multiplexing调用中
doPendingFunctors();
if (frameFunctor_)
{
frameFunctor_();
}
}
LOGD("EventLoop 0x%0x stop looping", this);
looping_ = false;
std::ostringstream oss;
oss << std::this_thread::get_id();
std::string stid = oss.str();
LOGI("Exiting loop, EventLoop object: 0x%x , threadID: %s", this, stid.c_str());
}
这是one loop per thread关键的函数了,可以看到实际上可以拆为四个作用,doTimer()处理定义的定时器事件,poll(kPollTimeMs, &activeChannels_)是通过IO复用技术检测有事件的通道,handleEvent(pollReturnTime_)活动通道处理返回的各个事件,doPendingFunctors()处理额外的一些任务。定时器队列这里不讲,自己有兴趣可以去了解。我们看到poll返回了所有有事件的activeChannels,之后Channel处理这些IO事件,当是侦听socket时,最终调用Acceptor::handleRead,当是客户端socket时,调用TcpConnection::handleRead。然后doPendingFunctors可以处理其他一些任务,比如客户端A给给客户端B发送消息,这当然执行在接受客户端A然后创建Session的线程中,我们在A的线程中找到了B的TcpConnection,进而找到了B的EventLoop,然后将这个发送任务加入B线程任务队列,也就是需要执行doPendingFunctors,但是程序阻塞在了poll返回函数里,执行不到doPendingFunctors,那么怎么办?于是我们给之前创建的唤醒fd发消息,唤醒poll,进而通过dpPendingFunctor处理发送消息,这里就是刚才创建唤醒的fd的作用。
给loop分派任务:
void EventLoop::runInLoop(const Functor& cb)
{
if (isInLoopThread())//如果是本线程,则直接运行,否则加入队列,等待运行
{
cb(); //等于accept::listen
}
else
{
queueInLoop(cb);
}
}
void EventLoop::queueInLoop(const Functor& cb)
{
{
std::unique_lock<std::mutex> lock(mutex_);
pendingFunctors_.push_back(cb);
}
if (!isInLoopThread() || callingPendingFunctors_)
{
wakeup();
}
}
和上面说的一样,外部通过runInloop给当前loop分配任务,在当前线程则直接执行,其他线程分到任务队列,然后唤醒fd,之后再处理。
其他的对Channel操作:
bool EventLoop::updateChannel(Channel* channel)
{
//assert(channel->ownerLoop() == this);
if (channel->ownerLoop() != this)
return false;
assertInLoopThread();
return poller_->updateChannel(channel);
}
void EventLoop::removeChannel(Channel* channel)
{
//assert(channel->ownerLoop() == this);
if (channel->ownerLoop() != this)
return;
assertInLoopThread();
if (eventHandling_)
{
//assert(currentActiveChannel_ == channel || std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());
}
LOGD("Remove channel, channel = 0x%x, fd = %d", channel, channel->fd());
poller_->removeChannel(channel);
}
bool EventLoop::hasChannel(Channel* channel)
{
//assert(channel->ownerLoop() == this);
assertInLoopThread();
return poller_->hasChannel(channel);
}
重要的都已经讲完了,其他的还有一些对poll的注册活动通道的事件,没什么难点,看看就行了。