红黑树实现定时器
采用红黑树实现以时间序为触发任务排序的方式的定时器
一、定时器的接口
- 获取当前时间
- 添加定时器
- 删除定时器
- 检查定时器
- 最近触发任务离当前的时间
二、定义接口
struct TimerNode;
struct TimerNodeBase
{
time_t expire; //不能唯一确定timernode
int64_t id; //唯一标识TimerNode的id
};
// c++14特性 防止成员中不必要的拷贝(例如:查找对比时)
struct TimerNode : public TimerNodeBase
{
using CallBack = std::function<void(const TimerNode &)>;
CallBack func;
};
//利用了多态
bool operator<(const TimerNodeBase &a, const TimerNodeBase &b);
static int64_t gid =0 ;
class Timer
{
public:
//返回的是当前的时间
static time_t GETicks();
//得到TimerNode id
int64_t GetId();
//添加定时器 返回Base就可以唯一确定定时任务,以便后续的删除
TimerNodeBase addTimer(time_t msec, TimerNode::CallBack func); //第一个参数传入定时的时间大小 第二个参数传入回调函数 也就是定时任务
//删除定时器 通过TimerNodeBase
bool delTimer(TimerNodeBase &node);
//检查定时器
bool checkTimer();
//最近触发的当前任务里现在有多久
time_t Timer_to_sleep();
// private:
std::set<TimerNode, std::less<>> timermap; //采用红黑树来存储时间结点 set的底层实现是红黑树 相比于map和mutile类容器较好
};
实现细节:
- 每一个时间节点有自己的超时时间点,id,还有超时后的回调函数。
- 时间节点采用了子类继承父类的方式实现,父类TimerBase中只存放期超时时间点和唯一标识定时器的id(常用于比较且比较稳定一创建就确定下来了),子类中存放回调函数(一般不需要常用来比较),以至于该结构以后扩充不会显得臃肿,也可以减少查找定时器的时候回调函数的拷贝次数。
- 采用了set容器,因为定时器是唯一的,不能一个key对应多核value,本身即是key也是value所以红黑树实现中set最佳(红黑树是一颗平衡二叉搜索树)
- 用一个全局静态变量gid唯一标识定时器,重载operator<按照时间序排序插入,时间相同,按照gid进行排序插入
三、接口的实现
//
// Created by root on 10/15/22.
//
#include"../include/Timer.h"
bool operator<(const TimerNodeBase &a, const TimerNodeBase &b)
{
if (a.expire < b.expire)
return true;
else if (a.expire > b.expire)
return false;
else
return a.id < b.id; //此时expire是相等的 前插入的放在左边 后插入的放在右边
}
inline time_t Timer::GETicks() {
//返回的是当前以毫秒为单位的时间
// steady指的是程序 也可以哟过来测试程序运行的时间
//强转成毫秒
auto sc = chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now()); //获取当前的时间点(程序运行开始的时候为起始点)
auto current = chrono::duration_cast<chrono::milliseconds>(sc.time_since_epoch()); //获取时间间隔(相当于程序开始到现在的时间)
return current.count();
}
inline int64_t Timer::GetId()
{
return gid++;
}
TimerNodeBase Timer::addTimer(time_t msec, TimerNode::CallBack func) //第一个参数传入定时的时间大小 第二个参数传入回调函数 也就是定时任务
{
TimerNode node;
node.func = func;
node.expire = GETicks() + msec; //超时时间点
node.id = GetId();
// 把node加载到一个存储的数据结构
timermap.insert(node);
return static_cast<TimerNodeBase>(node);
}
bool Timer::delTimer(TimerNodeBase &node)
{
auto it = timermap.find(node);
if (it != timermap.end())
{
timermap.erase(it);
return true;
}
return false;
}
bool Timer::checkTimer()
{
//找到某个数据结构最小的结点
auto iter = timermap.begin();
if (iter != timermap.end() && iter->expire <= GETicks())
{
iter->func(*iter);
timermap.erase(iter);
return true;
}
return false;
}
time_t Timer::Timer_to_sleep()
{
auto iter = timermap.begin();
if (iter == timermap.end())
{
return -1;
}
time_t gap = iter->expire - GETicks();
return gap > 0 ? gap : 0;
}
实现细节:
- 获取当前时间,用steady_lock获取当前时间,利用程序开始执行到当前时间的时间间隔作为时间点,精度是毫秒
- CallBack回调函数利用function函数对象使之灵活性变高