一、EventLoop
one loop per thread,每个线程最多一个。
使用范例:
muduo::InetAddress listenAddr(9981);
muduo::EventLoop loop;
muduo::TcpServer server(&loop, listenAddr);
server.setConnectionCallback(onConnection);
server.setMessageCallback(onMessage);
if (argc > 1) {
server.setThreadNum(atoi(argv[1]));
}
server.start();
loop.loop();
作用:
- 开启一个loop循环,定期检测是否有活跃的通道事件,有则处理,无在不处理。
- loop循环中除了处理真实的通道事件,还会处理添加到Eventloop队列中的回调函数。
- 处理定时器事件。
- 更新通道到Poll中,或者移除Poller中的通道。
二、源码分析
Eventloop.h
class EventLoop : boost::noncopyable
{
public:
typedef boost::function<void()> Functor;
EventLoop();
~EventLoop(); // force out-line dtor, for scoped_ptr members.
///
/// Loops forever.
///
/// Must be called in the same thread as creation of the object.
///
void loop(); //开始loop
void quit(); //退出loop
///
/// Time when poll returns, usually means data arrivial.
///
Timestamp pollReturnTime() const { return pollReturnTime_; }
int64_t iteration() const { return iteration_; }
/// Runs callback immediately in the loop thread.
/// It wakes up the loop, and run the cb.
/// If in the same loop thread, cb is run within the function.
/// Safe to call from other threads.
void runInLoop(const Functor& cb);
/// Queues callback in the loop thread.
/// Runs after finish pooling.
/// Safe to call from other threads.
void queueInLoop(const Functor& cb);
// timers
///
/// Runs callback at 'time'.
/// Safe to call from other threads.
///
TimerId runAt(const Timestamp& time, const TimerCallback& cb);
///
/// Runs callback after @c delay seconds.
/// Safe to call from other threads.
///
TimerId runAfter(double delay, const TimerCallback& cb);
///
/// Runs callback every @c interval seconds.
/// Safe to call from other threads.
///
TimerId runEvery(double interval, const TimerCallback& cb);
///
/// Cancels the timer.
/// Safe to call from other threads.
///
void cancel(TimerId timerId);
// internal usage
void wakeup();
void updateChannel(Channel* channel);
void removeChannel(Channel* channel);
// pid_t threadId() const { return threadId_; }
void assertInLoopThread()
{
if (!isInLoopThread())
{
abortNotInLoopThread();
}
}
bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }
// bool callingPendingFunctors() const { return callingPendingFunctors_; }
bool eventHandling() const { return eventHandling_; }
static EventLoop* getEventLoopOfCurrentThread();
private:
void abortNotInLoopThread();
void handleRead(); // waked up
void doPendingFunctors();
//
void printActiveChannels() const; // DEBUG
typedef std::vector<Channel*> ChannelList;
bool looping_; /* atomic */ //当前是否在loop
bool quit_; /* atomic */ //是否退出
bool eventHandling_; /* atomic */ //当前是否在处理事件
bool callingPendingFunctors_; /* atomic */ //队列中是否有待处理函数
int64_t iteration_; //FIXME ?
const pid_t threadId_; //线程ID
Timestamp pollReturnTime_; //Poll返回时间? 干什么用的?
boost::scoped_ptr<Poller> poller_; //Poller方法
boost::scoped_ptr<TimerQueue> timerQueue_; //定时器队列
int wakeupFd_; //唤醒fd
// unlike in TimerQueue, which is an internal class,
// we don't expose Channel to client.
boost::scoped_ptr<Channel> wakeupChannel_; //唤醒通道
ChannelList activeChannels_; //活跃的通道列表
Channel* currentActiveChannel_; //当前活跃的通道列表
MutexLock mutex_;
std::vector<Functor> pendingFunctors_; // @BuardedBy mutex_ //队列中待处理的方法
}
EventLoop.cc
namespace
{
__thread EventLoop* t_loopInThisThread = 0;
//Poll默认时间
const int kPollTimeMs = 10000;
//创建事件fd
int createEventfd()
{
int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
if (evtfd < 0)
{
LOG_SYSERR << "Failed in eventfd";
abort();
}
return evtfd;
}
#pragma GCC diagnostic ignored "-Wold-style-cast"
//屏蔽SigPipe信号,防止中断退出
class IgnoreSigPipe
{
public:
IgnoreSigPipe()
{
::signal(SIGPIPE, SIG_IGN);
LOG_TRACE << "Ignore SIGPIPE";
}
};
#pragma GCC diagnostic error "-Wold-style-cast"
IgnoreSigPipe initObj;
}
EventLoop* EventLoop::getEventLoopOfCurrentThread()
{
return t_loopInThisThread;
}
//EventLoop初始化时
//a、looping、quit_、eventHanding_、callingPendingFunctors_、iteration_默认初始化false
//b、设置当前线程ID。threadId_
//c、创建一个poll
//d、创建一个定时器队列
//e、创建一个唤醒事件fd
//f、创建一个唤醒事件通道
EventLoop::EventLoop()
: looping_(false),
quit_(false),
eventHandling_(false),
callingPendingFunctors_(false),
iteration_(0),
threadId_(CurrentThread::tid()),
poller_(Poller::newDefaultPoller(this)),
timerQueue_(new TimerQueue(this)),
wakeupFd_(createEventfd()),
wakeupChannel_(new Channel(this, wakeupFd_)),
currentActiveChannel_(NULL)
{
LOG_TRACE << "EventLoop created " << this << " in thread " << threadId_;
//设置当前loop的地址,
//若已有就不设置。
if (t_loopInThisThread)
{
LOG_FATAL << "Another EventLoop " << t_loopInThisThread
<< " exists in this thread " << threadId_;
}
else
{
t_loopInThisThread = this;
}
//设置唤醒通道的回调读,FIXME 这里设计应该有其它用途
wakeupChannel_->setReadCallback(
boost::bind(&EventLoop::handleRead, this));
// we are always reading the wakeupfd
wakeupChannel_->enableReading();
}
//析构就是关闭句柄,
//将t_loopInThisThread置位空地址
EventLoop::~EventLoop()
{
::close(wakeupFd_);
t_loopInThisThread = NULL;
}
//开始loop流程
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread();
//设置下状态位
looping_ = true;
quit_ = false;
LOG_TRACE << "EventLoop " << this << " start looping";
while (!quit_)
{
//定期查询是否有活跃的通道
activeChannels_.clear();
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
//FIXME_hqb 这个标志是做什么的?
++iteration_;
//日志级别开启时才会去循环输出打印
//避免未开启对应日志级别情况下去执行
//不必要的循环。
if (Logger::logLevel() <= Logger::TRACE)
{
printActiveChannels();
}
// TODO sort channel by priority
//当前要处理下事件,置位状态。使用结果在下文的removeChannel()体现
eventHandling_ = true;
//若有活跃的通道,则处理下,并将当前时间也传给下层
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it)
{
currentActiveChannel_ = *it;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
currentActiveChannel_ = NULL;
eventHandling_ = false;
//处理下队列中是否有需要执行的回调方法
doPendingFunctors();
}
//记录下退出标志,并设置状态位
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}
void EventLoop::quit()
{
//loop退出
//a、状态置位
//b、如果在当前线程,就唤醒下fd
quit_ = true;
if (!isInLoopThread())
{
wakeup();
}
}
void EventLoop::runInLoop(const Functor& cb)
{
//在EventLoop中执行回调函数
//a、如果Loop在自身当前线程,直接执行
//b、如果Loop在别的线程,则将回调放到Loop的
// 方法队列,保证回调在Loop所在的线程中执行
if (isInLoopThread())
{
cb();
}
else
{
queueInLoop(cb);
}
}
void EventLoop::queueInLoop(const Functor& cb)
{
//将回调函数放到待执行队列
{
MutexLockGuard lock(mutex_);
pendingFunctors_.push_back(cb);
}
//唤醒条件参考下文总结。
if (!isInLoopThread() || callingPendingFunctors_)
{
wakeup();
}
}
//将执行回调添加到定时器队列
TimerId EventLoop::runAt(const Timestamp& time, const TimerCallback& cb)
{
return timerQueue_->addTimer(cb, time, 0.0);
}
TimerId EventLoop::runAfter(double delay, const TimerCallback& cb)
{
Timestamp time(addTime(Timestamp::now(), delay));
return runAt(time, cb);
}
TimerId EventLoop::runEvery(double interval, const TimerCallback& cb)
{
Timestamp time(addTime(Timestamp::now(), interval));
return timerQueue_->addTimer(cb, time, interval);
}
//取消某个定时器事件
void EventLoop::cancel(TimerId timerId)
{
return timerQueue_->cancel(timerId);
}
//更新通道
void EventLoop::updateChannel(Channel* channel)
{
//Channel所在的Loop要和当前Loop保持一致
assert(channel->ownerLoop() == this);
//Loop所在线程不一致时,输出日志
assertInLoopThread();
//更新通道事件
poller_->updateChannel(channel);
}
//移除通道
void EventLoop::removeChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
if (eventHandling_)
{
//当要移除通道时,如果eventHandling_为true表示
//正在Eventloop::loop()中,此时channel不能是正在
//处理的currentActiveChannel,也不能再活跃的通道列表中
//即activeChannels中。
assert(currentActiveChannel_ == channel ||
std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());
}
//移除通道
poller_->removeChannel(channel);
}
//
void EventLoop::abortNotInLoopThread()
{
LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop " << this
<< " was created in threadId_ = " << threadId_
<< ", current thread id = " << CurrentThread::tid();
}
//这里用到的一个设计就是添加了
//回调函数到队列,通过往fd写个
//标志来通知,让阻塞的Poll立马
//返回去执行回调函数。
//唤醒下,写一个8字节数据
void EventLoop::wakeup()
{
uint64_t one = 1;
ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);
if (n != sizeof one)
{
LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8";
}
}
//读下8字节数据
void EventLoop::handleRead()
{
uint64_t one = 1;
ssize_t n = sockets::read(wakeupFd_, &one, sizeof one);
if (n != sizeof one)
{
LOG_ERROR << "EventLoop::handleRead() reads " << n << " bytes instead of 8";
}
}
void EventLoop::doPendingFunctors()
{
//处理队列中的回调函数
std::vector<Functor> functors;
//FIXME_hqb 状态标志的作用是什么
callingPendingFunctors_ = true;
//交换出来,避免调用时长时间占用锁
{
MutexLockGuard lock(mutex_);
functors.swap(pendingFunctors_);
}
for (size_t i = 0; i < functors.size(); ++i)
{
functors[i]();
}
callingPendingFunctors_ = false;
}
//输出下活跃的通道对应的事件,用于调试
void EventLoop::printActiveChannels() const
{
for (ChannelList::const_iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it)
{
const Channel* ch = *it;
LOG_TRACE << "{" << ch->reventsToString() << "} ";
}
}
三、关键点解析
1、唤醒方式
在EventLoop的IO线程内执行某个用户的任务回调,即
void EventLoop::runInLoop(const Functor& cb)
如果用户在当前IO线程调用这个函数,回调会同步执行,如果用户在其他线程调用runInLoop(),cb会被加入队列,IO线程会被唤醒来调用这个Functor。唤醒的方式一般有三种。
- pipe,使用fd[0] 为读端,fd[1]为写端,半双工。等待线程关注fd[0]的可读事件。
- socketpair,也有一对文件描述符,可用于双向通信,全双工。
- eventfd。eventfd是一个比pipe更高效的线程间事件通知机制,一方面它比pipe少用一个pipe
descriptor,节省了资源。另一方面,eventfd的缓冲区管理也简单的多,全部“buffer"只有定长8bytes,
不想pipe那样可能有不定长的真正buffer。
2、回调函数放在当前IO线程执行目的
可以不用锁的前提下,保证线程安全。
3、什么时候唤醒
void EventLoop::queueInLoop(const Functor& cb)
{
{
MutexLockGuard lock(mutex_);
pendingFunctors_.push_back(cb);
}
if (!isInLoopThread() || callingPendingFunctors_)
{
wakeup();
}
}
这里唤醒有两个条件:
- isInLoopThread(), 如果调用的queneInLoop的线程不是IO线程,那么唤醒。
- callingPendingFunctors_, 如果在IO线程调用queueInLoop(),而此时正在调用pending functor,那么唤醒也是必要的。
只有在IO线程的事件回调中调用queueInLoop(),才无须wakeup()。
4、不在handleRead()中执行doPendingFunctors()原因
添加回调函数到队列可以调用如下两种方法:
void EventLoop::runInLoop(const Functor& cb)
void EventLoop::queueInLoop(const Functor& cb)
如果 doPendingFunctors() 放在handleRead中执行,
1、如果用户调用的 EventLoop::runInLoop() 方法还好,会检测唤醒。
2、如果在当前IO线程,用户调用的是 EventLoop::queueInLoop() 回调函数不会立马被执行,需要等EventLoop::wakeup唤醒才会,这样就会存在很大的延迟。
备注:
参考链接 https://blog.csdn.net/FreeeLinux/article/details/53510541