C++ 实现定时器

news/2025/1/7 12:32:31/文章来源:https://www.cnblogs.com/BryceAi/p/18653738

冬天的午后,寒意略显温柔,不像晨时那样刺骨,也不像入夜之时的冰冷。阳光倾斜落在阳台上。想必它是耀眼的,照着屋外树梢上仅剩的几片叶子上,闪闪发光,有些晃眼。

学习自:零声教育的视频

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/864517.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kali安装pdtm工具

kali安装pdtm工具 前言 今天想安装一下pdtm工具集的,但过程中一直出现各种错误,找了几篇文章之后并没有找到解决方法,后解决之后写了这样一篇文章希望可以解决大家在安装过程中碰到的部分问题 介绍 pdtm(ProjectDiscovery Tool Manager)是专为简化ProjectDiscovery旗下一系…

组合逻辑电路的分析

组合逻辑电路:任何时刻电路的输出状态只取决于该时刻的输入状态,而与该时刻以前的电路状态无关。 组合逻辑电路的分析 分析步骤由逻辑图写出输出端的逻辑表达式 运用逻辑代数化简或变换 列逻辑状态表 分析逻辑功能例题1 分析下图的逻辑功能写出逻辑表达式\[Y=\overline{Y_{2}…

floating panel - 带有锚点功能和可拖拽顶栏的浮动面板

在现代的前端应用中,浮动面板是一个非常常见的UI组件,它能够为用户提供额外的信息和操作空间,同时又不会占据页面的主要内容区域。本文将详细记录如何实现一个带有锚点功能和可拖拽顶栏的浮动面板。设计思路 这个浮动面板将由三个主要部分组成:父元素(floating-panel)、标…

20241413《计算机基础与程序设计》课程总结

每周作业链接汇总 1.第一周作业 简要内容:学习“基于VirtualBox虚拟机安装Ubuntu图文教程”在自己笔记本上安装Linux操作系统、学习掌握二进制,十进制和十六进制之间的转换、快速浏览《计算机和学概论》,并对每章提出了自己的疑问3.第三周作业 简要内容:数字分类与计数法 位…

Python学习(五)——配套《PyTorch深度学习实战》

1. Python的流程控制tips:我使用的Python3.9版本,if、else是要加:的 Python的流程控制主要通过条件语句和循环语句来实现,它们允许程序根据特定的条件执行不同的代码块。以下是Python中常用的流程控制结构:条件语句(if-elif-else) 条件语句允许程序根据条件的真假来选择执…

MOSFET 场效应管:IRF4905(P沟道)、IRF3205(N沟道)

在 H 桥电路中,同时用到 IRF4905、IRF3205 。G - D - S Gate 栅极, Drain 漏极, Source 源极 IRF4905IRF3205P沟道N沟道 通过调控 栅极G 电压(相对 源S),形成电场,从而控制 漏D-源S 电流。 P沟道的源极S接输入,漏极D导通输出 触发:V GS th , -4.0 ~ -2.0 V 。举例:(详…

为了解决服务启动慢的问题,我为什么要给Apollo和Spring提交PR?

最近在整理之前记录的工作笔记时,看到之前给团队内一组服务优化启动耗时记录的笔记,简单整理了一下分享出来。问题原因并不复杂,主要是如何精准测量和分析,优化后如何定量测量优化效果,说人话就是用实际数据证明优化效果。 背景 团队内有一组服务启动明显较其它服务要慢(…

【MATLAB】自学记录之读取DEM高程数据文件并渲染成三维地形图

1. 前言 近日在学习MATLAB编程以及地理高程数据处理等相关知识时,希望通过MATLAB的绘图等相关函数,读取高程数据文件,最后以可视化的方式展示全球陆地范围内的三维高程数据图。 2. 运行环境及数据序号 配置项 说明1 CPU Intel i5-12490F2 内存 16G*2, 3600MHz3 磁盘 256G,S…

Redpanda Console - 流数据管理控制台

Redpanda Console - 流数据管理控制台 简介 Redpanda是一个与Kafka兼容的流媒体数据平台,该平台具有高性能、操作友好和云就绪性。这家总部位于旧金山的公司成立于2019年,专注于Kafka公司关键任务系统的替代产品。 Redpanda使用C++重写Kafka,与Kafka API完全兼容,可以与所有…

Prometheus+Grafana监控flink任务指标

Prometheus+Grafana监控flink任务指标 前期准备 Prometheus 是一款基于时序数据库的开源监控告警系统,由go语言开发,Prometheus的基本原理是通过HTTP协议周期性抓取被监控组件的状态,任意组件只要提供对应的HTTP接口就可以接入监控。 Grafana 是一款采用Go语言编写的开源应用…

Visual Studio中的C#项目连接本地mysql数据库

一、给项目搭载Mysql连接所需的NuGet包 1.点击visual studio上方导航栏的"工具"选项 2.在下拉框中选中"NuGet包管理器" 3.在展开的侧边栏中选择"管理解决方案的 NuGet 程序包" 4.在新打开的窗口中选择"浏览" 5.搜索框中输入"mysq…

P6822 [PA 2012 Finals] Tax

一个小Trick。很牛的题目!! 直接做可能比较困难,你要考虑一些东西重构一下原图。 这一个题目因为是与边相关的,考虑拆边,拆成两条有向边,那么对于一个点的贡献,我们暴力枚举他的边,两条边的贡献就是取 max。 但这个显然过不了,我们有一种差分建边的方式,按照边权排序…