红黑树实现定时器

红黑树实现定时器

采用红黑树实现以时间序为触发任务排序的方式的定时器

一、定时器的接口

  1. 获取当前时间
  2. 添加定时器
  3. 删除定时器
  4. 检查定时器
  5. 最近触发任务离当前的时间

二、定义接口

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类容器较好
};

实现细节:

  1. 每一个时间节点有自己的超时时间点,id,还有超时后的回调函数。
  2. 时间节点采用了子类继承父类的方式实现,父类TimerBase中只存放期超时时间点和唯一标识定时器的id(常用于比较且比较稳定一创建就确定下来了),子类中存放回调函数(一般不需要常用来比较),以至于该结构以后扩充不会显得臃肿,也可以减少查找定时器的时候回调函数的拷贝次数。
  3. 采用了set容器,因为定时器是唯一的,不能一个key对应多核value,本身即是key也是value所以红黑树实现中set最佳(红黑树是一颗平衡二叉搜索树)
  4. 用一个全局静态变量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;
}

实现细节:

  1. 获取当前时间,用steady_lock获取当前时间,利用程序开始执行到当前时间的时间间隔作为时间点,精度是毫秒
  2. CallBack回调函数利用function函数对象使之灵活性变高
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值