1. 说明
- 一个EventLoop类,任何一个线程,只要创建并运行了EventLoop,都称之为IO线程(为了方便使用,定义了EventLoopThread类,封装了IO线程)
- 使用了Channel,Poller,TimerQueue,Timestamp类
- noncopyable
2. 变量
-
类型定义
- Functor
- ChannelList
-
looping_; /* atomic */
- bool类型,表示是否处于loop()函数执行中,它的值变化都在loop()函数中
-
std::atomic quit_;
- bool类型,判断是否退出,loop()中循环的结束条件
-
bool eventHandling_; /* atomic */
- 表示是否处于处理事件事件中,在loop()中poll()选择需要处理的通道(事件)后,在对这些事件的处理过程中把这个变量置为true
-
bool callingPendingFunctors_; /* atomic */
- 是否处于doPendingFunctors()函数中
-
int64_t iteration_;
- 在构造函数中赋初值为0,以后只在loop()中有用到,即每次poll()后+1,应该是记录poll()的次数吧
-
const pid_t threadId_
- 当前对象所属线程id
-
Timestamp pollReturnTime_;
- 调用poll(),返回时间戳
-
std::unique_ptr poller_;
- 每个EventLoop都会有一个Poller,
-
std::unique_ptr timerQueue_;
- 每个EventLoop都有个TimerQueue,定时器列表,作为定时器
-
int wakeupFd_;
- 用于eventfd,在构造函数中用createEventfd()生成eventfd赋值,作为线程唤醒需要使用的fd
- 进程间通信有几种方式:pipe,socket,eventfd,线程间通信还有条件变量等
-
std::unique_ptr wakeupChannel_;
- 该通道将会纳入poller_来管理,用来唤醒当前线程,这是一个内部channel,不会暴露给用户
-
boost::any context_;
- boost::any,任意数据类型,C++17也加入C++标准
-
ChannelList activeChannels_;
- Poller返回的活动通道
-
Channel* currentActiveChannel_;
- 当前正在处理的活动通道
-
mutable MutexLock mutex_;
- 锁,没什么说的
-
std::vector pendingFunctors_ GUARDED_BY(mutex_)
- Functor的队列,这个就是需要处理的functor队列,在doPendingFunctors()函数中处理完就清空了,要理解需要结合runInLoop(),queueInLoop()等函数
3. 函数
1. 私有
-
void abortNotInLoopThread();
- 打印日志并退出,当前线程和此对象线程不一样,在assertInLoopThread()中调用
-
void handleRead(); // waked up
- 唤醒处理函数,调用read,里面的内容没有什么意义
-
void doPendingFunctors();
- 不是简单的在临界区内依次调用Functor,而是把回调列表swap到functors中,这样一方面减小了临界区的长度(意味着不会阻塞其他线程的queueInLoop()),另一方面也避免了死锁(因为Functor可能再次调用queueInLoop())
- 把pendingFunctors_交换到新的局部空间,然后逐个调用这些函数
-
void printActiveChannels() const;
- 把活动的channel信息打印到日志
2. 共有
-
构造
- 变量的初始化,设置当前channel的读回调函数为私有函数handleRead
-
析构
- 释放资源
-
void loop()
- 循环调用poller_->poll(),返回需要处理的通道channel,处理这些通道的回调函数
- 最后doPendingFunctors(),处理其他线程或者当前线程异步增加的需要处理的函数,详见下面的runInLoop()和queueInLoop(),这样让IO线程也能处理一些计算任务
-
void quit()
- 把quit_置为true,如果不是由当前IO线程调用的,要先用wakeup()唤醒当前线程
-
Timestamp pollReturnTime()
- 返回pollReturnTime_
-
int64_t iteration()
- 返回iteration_
-
void runInLoop(Functor cb)
- 在IO线程中执行某个回调函数,该函数可以跨线程调用
- 如果在当前IO线程调用runInLoop.则同步调用cb()回调函数
- else 如果在其他线程调用runInLoop.则调用queueInLoop(cb)异步地将cb添加到队列
-
void queueInLoop(Functor cb)
- 如果调用当前行数的线程不是当前IO线程,则需要唤醒,或者是当前线程,但此时正在调用pendingfunctor,也需要唤醒
-
size_t queueSize()
- 返回pendingFunctors_.size()
-
TimerId runAt(Timestamp time, TimerCallback cb)
- 就是把cb()加入定时器队列,在time时间执行
-
TimerId runAfter(double delay, TimerCallback cb)
- 把cd()加入定时器队列,当前时间+delay后执行
-
TimerId runEvery(double interval, TimerCallback cb)
- 把cb()加入定时器队列,每隔interval间隔时间执行一次
-
void cancel(TimerId timerId)
- 调用poller_->cancel(timerId),结束这个定时器任务
-
void wakeup()
- 唤醒当前对象线程,实际是调用write(),这样loop()函数中的poll()就会选到当前线程,然后执行
-
void updateChannel(Channel* channel);
- 断言当前对象线程正在执行,调用poller_->updateChannel(channel);
-
void removeChannel(Channel* channel);
- 保证能找到这个channel,调用poller_->removeChannel(channel);移除这个channel
-
bool hasChannel(Channel* channel)
- 调用poller_->hasChannel(channel);
-
void assertInLoopThread()
- 如果当前线程不是本对象线程,就调用abortNotInLoopThread(),打印日志并退出
-
bool isInLoopThread()
- 就是判断threadId_和当前正在运行的线程id是否相同
-
bool eventHandling()
- 返回eventHandling_,看是否处于loop()处理事件过程中
-
void setContext(const boost::any& context)
- 如名
-
const boost::any& getContext()
- 如名
-
boost::any* getMutableContext()
- 如名
-
static EventLoop* getEventLoopOfCurrentThread()
- 静态函数,返回t_loopInThisThread,即当前EventLoop对象
4. 全局
-
t_loopInThisThread
- 线程局部变量,在构造函数中赋值为当前对象
-
kPollTimeMs
- 在poll()中当参数传过去
-
createEventfd()
- 调用eventfd()生成eventfd返回
-
IgnoreSigPipe类
- 构造函数中调用signal()忽略SIGPIPE信号
- 并且全局已经定义这个类了:initObj变量,也就是已经调用这个signal()了