基于小根堆实现的定时器,关闭超时的非活动连接


简介

定义了一个基于最小堆(Min Heap)的定时器类HeapTimer,用于管理一组定时任务。每个定时任务都包含一个ID、一个超时时间戳和一个回调函数。当达到定时任务的超时时间时,相应的回调函数会被执行。下面是对代码的详细解释:

头文件和命名空间

  • 包含了一些必要的头文件,如<vector>, <unordered_map>, <functional>, <chrono>等。
  • 使用了std命名空间。

类型别名

  • TimeoutCallBack:定义了一个无参数函数对象类型,用于存储超时时的回调函数。
  • Clock:指定了高精度时钟std::chrono::high_resolution_clock作为计时器的基础。
  • MS:定义了一个毫秒类型std::chrono::milliseconds
  • TimeStamp:定义了一个时间戳类型,即Clocktime_point

TimerNode 结构体

  • TimerNode结构体用于存储每个定时任务的信息。
  • 包含三个成员:id(任务ID)、expires(超时时间戳)和cb(超时时的回调函数)。
  • 重载了<运算符,以便在堆中使用。

HeapTimer 类

  • HeapTimer类用于管理定时任务。
  • 成员变量:
    • heap_:一个std::vector容器,用于存储TimerNode对象,并实现最小堆。
    • ref_:一个std::unordered_map,用于快速查找特定ID的定时任务在heap_中的位置,建立id与index之间的映射。
  • 构造函数和析构函数:
    • 构造函数HeapTimer()预分配了64个TimerNode对象的空间。
    • 析构函数~HeapTimer()清除所有定时任务。
  • 公开成员函数:
    • adjust(int id, int newExpires):调整指定ID的定时任务的超时时间。
    • add(int id, int timeOut, const TimeoutCallBack& cb):添加一个新的定时任务。
    • doWork(int id):执行指定ID的定时任务并从堆中移除。
    • clear():清除所有定时任务。
    • tick():检查并执行所有到期的定时任务。
    • pop():移除并处理堆顶(最早超时)的任务。
    • GetNextTick():获取下一个定时任务的剩余时间(毫秒)。
  • 私有成员函数:
    • del_(size_t i):从堆中移除指定位置的节点。
    • siftup_(size_t i):上浮操作,用于在堆中插入新节点或调整节点位置。
    • siftdown_(size_t index, size_t n):下沉操作,用于在堆中调整节点位置。
    • SwapNode_(size_t i, size_t j):交换堆中两个节点的位置。

使用方式

  • 添加定时任务:使用add方法添加定时任务,需要提供任务的ID、超时时间(单位通常是毫秒)和一个TimeoutCallBack类型的回调函数,当任务超时时会被调用。
  • 执行定时任务:定期调用tick方法,这会检查堆顶任务是否已超时,并执行相应的回调函数。
  • 查询剩余时间:使用GetNextTick方法获取下一个定时任务的剩余时间(毫秒)。
  • 修改任务超时时间:如果需要调整某个任务的超时时间,使用adjust方法。
  • 手动执行和删除任务:通过doWork方法可以直接执行并删除指定ID的任务。
  • 清除所有任务:调用clear方法可以清除所有定时任务。
  • 实时监控任务状态:可以根据GetNextTick返回的结果动态决定程序的行为,比如决定主线程是否应该休眠等待还是继续其他工作。

使用HeapTimer类的过程主要包括以下几个步骤:

  1. 创建HeapTimer实例

    HeapTimer timer;
    
  2. 添加定时任务

    // 假设有一个回调函数myCallback
    void myCallback() {
        // 任务执行的具体逻辑
    }
    
    // 添加一个定时任务,ID为1,超时时间为5000毫秒,超时时调用myCallback
    timer.add(1, 5000, myCallback);
    
  3. 周期性调用tick()方法

    while (true) {
        timer.tick();  // 检查并执行所有已到期的任务
        
        // 可以在这里做其他工作,比如休眠一段时间避免频繁检查
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
  4. 动态调整任务超时时间

    // 如果需要延长或缩短某个任务的超时时间
    timer.adjust(1, 7000);  // 把ID为1的任务超时时间调整为7000毫秒
    
  5. 获取下一个任务的剩余超时时间

    int nextTick = timer.GetNextTick();
    if (nextTick > 0) {
        // 还有任务未到期,可以根据nextTick决定程序应该休眠多久
        std::this_thread::sleep_for(std::chrono::milliseconds(nextTick));
    }
    
  6. 手动执行并移除某个任务

    timer.doWork(1);  // 执行ID为1的任务并从堆中移除
    
  7. 清除所有定时任务

    timer.clear();  // 清除所有定时任务
    

请注意,为了确保定时任务能够正常执行,通常会将其应用在一个循环中持续运行,同时结合线程同步机制(例如条件变量或事件循环)来控制程序的休眠与唤醒。另外,这个类并没有提供多线程安全的访问控制,如果在多线程环境中使用,需要自行添加锁或其他并发控制机制。

注意事项

  • 该实现是基于最小堆的,因此在查找和移除最小(即最早到期)的任务时具有O(log N)的时间复杂度。
  • ref_哈希表用于快速查找特定ID的任务,其查找操作具有O(1)的平均时间复杂度。
  • 代码没有处理并发访问的问题,如果需要在多线程环境下使用,需要添加锁或其他同步机制来确保数据一致性。
posted @   guanyubo  阅读(261)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示