冬天的午后,寒意略显温柔,不像晨时那样刺骨,也不像入夜之时的冰冷。阳光倾斜落在阳台上。想必它是耀眼的,照着屋外树梢上仅剩的几片叶子上,闪闪发光,有些晃眼。
学习自:零声教育的视频
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; // 定时器节点 IDset<TimerNode, std::less<> > timerMap; // 定时器节点容器
};int64_t Timer::gid = 0; // 初始化定时器节点 IDint 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;
}