目录
muduo中定时器模块的特点
muduo中的定时器系统
muduo中定时器实现的逻辑:
Timer类
TimerQueue类
使用timerfd实现定时功能
timer_create
timerfd_settime
创建TimerQueue
删除定时器管理对象编辑
插入定时器流程
处理到期定时器
在muduo的定时器系统中,一共由四个类:Timestamp,Timer,TimeId,TimerQueue组成。其中最关键的是Timer和TimerQueue两个类。该项目中没有使用Timeld类。
TimerQueue类,是整个定时器设施的核心,其他三个类简介其作用。 其中Timestamp是一个以int64_t表示的微秒级绝对时间,而Timer则表示一个定时器的到时事件,是否具有重复唤醒的时间等,TimerId表示在在TimerQueue中对Timer的索引。
①:Timer类:Timer类包含了一个超时时间戳和一个回调函数。当超时时间戳到达时,调用回调函数出发定时事件。
②:TimerQueue类:TimerQueue类是一个基于事件戳排序的定时器容器。它使用了最小堆(MinHeap)数据结构来保证定时器按照超时时间的顺序进行排列。TimerQueue类提供了添加、删除和获取最近超时的定时器的接口。
③:EventLoop类:EventLoop类是muduo网络库的核心组件,负责事件的循环和处理。其中包括定时器事件的管理。EventLoop有一个成员变量TimerQueue timerQueue_,用于存储定时器对象。EventLoop会在事件循环中监测定时器队列中最近超时的定时器,并调用其回调函数。
对于不是一次性的定时器,我们通过restart方法,观察定时器的构造函数中
repeat_(interval > 0.0) // 一次性定时器设置为0
如果是需要重新利用的定时器,会调用restart方法,我们设置其下一次超时时间为「当前时间 + 间隔时间」。如果是「一次性定时器」,那么就会自动生成一个空的 Timestamp,其时间自动设置为 0.0
。
TimerQueue类管理作为管理定时器的结构。其内部使用 STL 容器 set 来管理定时器。我们以时间戳作为键值来获取定时器。set 内部实现是红黑树,红黑树中序遍历便可以得到按照键值排序过后的定时器节点。小顶堆,说明最小的事件戳(即最早出发的定时器位于容器顶部)
using Entry = std::pair
;
using TimerList = std::set;
①:整个TimerQueue之打开一个timefd,用以观察定时器队列队首的到期事件。其原因是因为set容器是一个有序队列,以<排序,就是说整个队列中,Timer的到期时间时从小到大排列的,正是因为这样,才能做到节省系统资源的目的。
②:整个定时器队列采用了muduo典型的事件分发机制,可以使得定时器的到期时间像fd一样在Loop线程中处理。
timerfd与IO多路复用机制(如epoll)结合使用,可以实现基于事件得事件驱动编程。当定时器到期时,可以通过epoll等机制监视timerfd得可读事件,并触发相应得事件处理逻辑。当超时事件发生时,该文件描述符就变为可读。
timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK | TFD_CLOEXEC);
int timerfd_settime(int fd,int flags
const struct itimerspec *new_value
struct itimerspec *old_value);
//成功返回0
1、EventLoop调用方法,加入一个定时器事件,传入定时器回调函数,超时时间和间隔时间(为0.0则为一次性定时器),addTimer方法根据这些属性构造新得定时器。
2、定时器队列内部插入此定时器,并判断这个定时器得超时时间是否比先前得都早。如果是最早触发的,就会调用resetTimerfd
方法重新设置tiemrfd_的触发时间。内部会根据超时时间和现在时间计算出新的超时时间。
内部实现的插入方法获取此定时器的超时时间,如果比先前的时间小就说明第一个触发。那么我们会设置好布尔变量。因此这涉及到timerfd_的触发时间。
重置timerfd_ :通过计算时间差使用timerfd_settime将新的到期时间设置到指定定时器事件得文件描述符上
reset
方法重新设置定时器①:ReadTimerFd读取定时器文件描述符(timerfd)的值:
②: getExpired获取已经到期的定时器列表:
④:重新设置定时器之前,调用reset
函数对已到期的定时器进行处理: