首页 > 编程语言 >C++ 实现定时器

C++ 实现定时器

时间:2025-01-05 19:10:54浏览次数:1  
标签:定时器 return 实现 brief C++ iter 超时 节点


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

学习自:零声教育的视频

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

相关文章

  • 【C++动态规划】2088. 统计农场中肥沃金字塔的数目|2104
    本文涉及知识点C++动态规划LeetCode2088.统计农场中肥沃金字塔的数目有一个矩形网格状的农场,划分为m行n列的单元格。每个格子要么是肥沃的(用1表示),要么是贫瘠的(用0表示)。网格图以外的所有与格子都视为贫瘠的。农场中的金字塔区域定义如下:区域内格子数......
  • RabbitMQ高级篇之发送者可靠性 发送者确认的代码实现
    文章目录一、配置文件中启用确认机制二、编写`returnCallback`和`confirmCallback`三、消息确认测试四、性能注意事项总结一、配置文件中启用确认机制在application.yml或application.properties文件中开启publishconfirm和publishreturn:publishcon......
  • 注册托管服务+timer实现简单定时任务
    1.创建服务类publicclassTimerService:IHostedService,IDisposable{///<summary>///定义定时器///</summary>privatestaticSystem.Threading.Timer_timer;privatestaticint_count=0;///<summary>......
  • uniapp - 详解使用高德地图在地图上实现绘制边界/点聚合/行政区域高亮等功能,Uniapp高
    效果图在uni-app手机h5网页网站/支付宝微信小程序/安卓app/苹果app/nvue等(全平台兼容)开发中,实现各端都兼容的“安装使用高德地图并实现点聚合/地图绘制边界部分高亮显示”,高德地图点聚合标记及高德地图绘制行政边界等,标点窗体信息展示,在高德地图上标点及卡片气泡框面板......
  • 4.3 C++对象模型和this指针
    4.3.1成员变量和成员函数分开存储只有非静态成员变量才属于类的对象上1.空对象占用的内存空间为1,为了区分空对象占用的位置2.非静态成员变量占用4个内存空间,属于类的对象上的3.静态成员变量static不占对象空间,不属于类的对象上的4.函数不占对象空间,所有函数共享一个......
  • python毕设 网盘系统的设计与实现程序+论文
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、选题背景随着信息技术的飞速发展,数据存储和管理需求日益增长。关于网盘系统的研究,现有研究主要以商业网盘的运营模式、大规模数据存储技术等为......
  • springboot毕设 基于BS的招聘网站的设计与实现 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,网络招聘已成为现代企业和求职者之间沟通的重要桥梁。传统的招聘方式,如招聘会、报纸广告等,已难以满足当前快速变化的就业市......
  • 《 C++ 点滴漫谈: 十七 》编译器优化与 C++ volatile:看似简单却不容小觑
    摘要本文深入探讨了C++中的volatile关键字,全面解析其基本概念、典型用途以及在现代编程中的实际意义。通过剖析volatile的核心功能,我们了解了它如何避免编译器优化对硬件交互和多线程环境中变量访问的干扰。同时,文章分析了volatile的局限性,如缺乏线程安全保障,并介......
  • 基于vue的商城小程序的毕业设计与实现(源码及报告)
     环境搭建  ☞☞☞​​​Vue入手篇(一),防踩雷(全网最详细教程)_vueforce-CSDN博客目录 一、功能介绍 二、登录注册功能 三、首页 四、项目截图 五、源码获取  一、功能介绍用户信息展示:页面顶部设有用户头像和昵称展示区,方便用户识别自己的账号信息。......
  • C++ 前缀和
    有一个数组{2,1,3,6,4},询问三次结果:a[5]={2,1,3,6,4}1.数组第1到第2个元素的和是多少?2.数组第1到第3个元素的和是多少?3.数组第2到第4个元素的和是多少?原始方法(无前缀和):1#include<iostream>2#include<stdio.h>3usingnamespacestd;4intmain(){5......