基于小根堆实现的定时器,关闭超时的非活动连接
简介
定义了一个基于最小堆(Min Heap)的定时器类HeapTimer
,用于管理一组定时任务。每个定时任务都包含一个ID、一个超时时间戳和一个回调函数。当达到定时任务的超时时间时,相应的回调函数会被执行。下面是对代码的详细解释:
头文件和命名空间
- 包含了一些必要的头文件,如
<vector>
,<unordered_map>
,<functional>
,<chrono>
等。 - 使用了
std
命名空间。
类型别名
TimeoutCallBack
:定义了一个无参数函数对象类型,用于存储超时时的回调函数。Clock
:指定了高精度时钟std::chrono::high_resolution_clock
作为计时器的基础。MS
:定义了一个毫秒类型std::chrono::milliseconds
。TimeStamp
:定义了一个时间戳类型,即Clock
的time_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
类的过程主要包括以下几个步骤:
-
创建HeapTimer实例
HeapTimer timer;
-
添加定时任务
// 假设有一个回调函数myCallback void myCallback() { // 任务执行的具体逻辑 } // 添加一个定时任务,ID为1,超时时间为5000毫秒,超时时调用myCallback timer.add(1, 5000, myCallback);
-
周期性调用tick()方法
while (true) { timer.tick(); // 检查并执行所有已到期的任务 // 可以在这里做其他工作,比如休眠一段时间避免频繁检查 std::this_thread::sleep_for(std::chrono::milliseconds(100)); }
-
动态调整任务超时时间
// 如果需要延长或缩短某个任务的超时时间 timer.adjust(1, 7000); // 把ID为1的任务超时时间调整为7000毫秒
-
获取下一个任务的剩余超时时间
int nextTick = timer.GetNextTick(); if (nextTick > 0) { // 还有任务未到期,可以根据nextTick决定程序应该休眠多久 std::this_thread::sleep_for(std::chrono::milliseconds(nextTick)); }
-
手动执行并移除某个任务
timer.doWork(1); // 执行ID为1的任务并从堆中移除
-
清除所有定时任务
timer.clear(); // 清除所有定时任务
请注意,为了确保定时任务能够正常执行,通常会将其应用在一个循环中持续运行,同时结合线程同步机制(例如条件变量或事件循环)来控制程序的休眠与唤醒。另外,这个类并没有提供多线程安全的访问控制,如果在多线程环境中使用,需要自行添加锁或其他并发控制机制。
注意事项
- 该实现是基于最小堆的,因此在查找和移除最小(即最早到期)的任务时具有O(log N)的时间复杂度。
ref_
哈希表用于快速查找特定ID的任务,其查找操作具有O(1)的平均时间复杂度。- 代码没有处理并发访问的问题,如果需要在多线程环境下使用,需要添加锁或其他同步机制来确保数据一致性。
Do not communicate by sharing memory; instead, share memory by communicating.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了