冬天的午后,寒意略显温柔,不像晨时那样刺骨,也不像入夜之时的冰冷。阳光倾斜落在阳台上。想必它是耀眼的,照在屋外树梢上仅剩的几片叶子上,闪闪发光,有些晃眼。
学习自:零声教育的视频
1. 什么是定时器
定时器是一种用于在未来某个时间点执行某个任务的机制。在操作系统中,定时器是一种非常重要的机制,它可以用于实现很多功能,比如定时任务、超时处理、心跳检测等。
2. 定时器的实现
#include <sys/epoll.h>
#include <iostream>
#include <functional>
#include <chrono>
#include <set>
#include <memory>
using namespace std;
/*
* 定时器
* 定时器即是在一段时间后执行某个操作,可以用于实现心跳检测、超时处理等功能
* 1. 使用 epoll_wait 的超时时间作为定时器的超时时间
* 2. 使用 set 容器存储定时器节点,每次检查定时器时,取出第一个节点,判断是否超时,超时则执行回调函数
*
*/
/*
* @brief 定时器节点基类
* @details 定时器节点基类,用于存储定时器节点的公共属性
* 1. expire : 超时时间
* 2. id : 定时器节点 ID
*/
struct TimerNodeBase
{
time_t expire;
int64_t id;
};
// C++ 14 新特性 find() , TimerNodeBase 是防止 TImerNode 多次拷贝 中 的 Callback 被多次复制 造成性能损失
/*
* @brief 定时器节点
* @details 定时器节点,继承自定时器节点基类,用于存储定时器节点的属性
* 1. func : 定时器回调函数,即定时器超时后执行的操作
*/
struct TimerNode : public TimerNodeBase
{
using Callback = std::function<void(const TimerNode &node)>;
Callback func;
};
/*
* @brief 定时器节点比较函数
* @details 定时器节点比较函数,用于 set 容器的排序
*/
bool operator < (const TimerNodeBase &lhd, const TimerNodeBase &rhd)
{
if(lhd.expire < rhd.expire) return true;
else if(lhd.expire > rhd.expire) return false;
return lhd.id < rhd.id;
}
/*
* @brief 定时器
* @details 定时器,用于添加、删除、检查定时器节点
* 1. GetTick : 获取当前时间戳
* 2. GenID : 生成定时器节点 ID
* 3. AddTimer : 添加定时器节点
* 4. DelTimer : 删除定时器节点
* 5. CheckTimer : 检查定时器节点
* 6. TimeToSleep : 获取定时器剩余时间
*/
class Timer
{
public:
/*
* @brief 获取当前时间戳
* @return 当前时间戳
*/
static time_t GetTick()
{
// 获取当前时间戳
auto sc = chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now());
// 转换为毫秒
auto temp = chrono::duration_cast<chrono::milliseconds>(sc.time_since_epoch());
return temp.count();
}
/*
* @brief 生成定时器节点 ID
* @return 定时器节点 ID
*/
static int64_t GenID()
{
return ++gid;
}
/*
* @brief 添加定时器节点
* @param msec : 超时时间
* @param func : 定时器回调函数
* @return 定时器节点
*/
TimerNodeBase AddTimer(time_t msec, TimerNode::Callback func)
{
TimerNode tn;
tn.expire = GetTick() + msec;
tn.func = func;
tn.id = GenID();
timerMap.insert(tn);
return static_cast<TimerNodeBase>(tn);
}
/*
* @brief 删除定时器节点
* @param node : 要删除的定时器节点
* @return 是否删除成功
*/
bool DelTimer(TimerNodeBase &node)
{
auto iter = timerMap.find(node);
if(iter != timerMap.end())
{
timerMap.erase(iter);
return true;
}
return false;
}
/*
* @brief 检查定时器节点
* @return 是否有超时的定时器节点
*/
bool CheckTimer()
{
auto iter = timerMap.begin();
if(iter != timerMap.end() && iter->expire <= GetTick())
{
iter->func(*iter);
timerMap.erase(iter);
return true;
}
return false;
}
/*
* @brief 获取定时器剩余时间
* @return 定时器剩余时间
*/
time_t TimeToSleep()
{
auto iter = timerMap.begin();
if(iter == timerMap.end()) {
return -1;
}
time_t diss = iter->expire - GetTick();
return diss > 0 ? diss : 0;
}
protected:
static int64_t gid; // 定时器节点 ID
set<TimerNode, std::less<> > timerMap; // 定时器节点容器
};
int64_t Timer::gid = 0; // 初始化定时器节点 ID
int main()
{
// 创建 epoll 实例
int epfd = epoll_create(1);
// 创建定时器实例
unique_ptr<Timer> timer = make_unique<Timer>();
// =============测试================
int n = 0;
timer->AddTimer(1000, [&](const TimerNode &node){
cout << "GetTick : " << Timer::GetTick() << " n : " << ++n << " node id : " << node.id << endl;
});
timer->AddTimer(1000, [&](const TimerNode &node){
cout << "GetTick : " << Timer::GetTick() << " n : " << ++n << " node id : " << node.id << endl;
});
auto node = timer->AddTimer(2000, [&](const TimerNode &node){
cout << "GetTick : " << Timer::GetTick() << " n : " << ++n << " node id : " << node.id << endl;
});
timer->AddTimer(3000, [&](const TimerNode &node){
cout << "GetTick : " << Timer::GetTick() << " n : " << ++n << " node id : " << node.id << endl;
});
timer->DelTimer(node);
// ===============================
// 创建 epoll 事件
epoll_event ev[64] = {0};
while (true)
{
// 等待 epoll 事件
int n = epoll_wait(epfd, ev, 64, timer->TimeToSleep()); // 使用定时器剩余时间作为超时时间
// 处理 epoll 事件
for (int i = 0; i < n; i++) {
// 处理事件逻辑
}
// 检查定时器
while (timer->CheckTimer()) {
// 不断触发已超时的定时器
}
}
return 0;
}
标签:定时器,return,实现,brief,C++,iter,超时,节点
From: https://www.cnblogs.com/BryceAi/p/18653738