C++ 实现定时器


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

学习自:零声教育的视频

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;
}
posted @ 2025-01-05 19:03  BryceAi  阅读(20)  评论(0编辑  收藏  举报