基于红黑树,时间轮,最小堆的定时器方案

定时器(容器+检测机制)

        为什么要有定时器
  •         精确控制任务执行时间
  •         实现异步操作
  •         任务调度和优先级管理
         定时器有什么作用
  •         组织管理大量延时任务的模块
  •         不过度占用线程,高效处理定时任务
        定时器怎么解决问题

        通过组织大量延时任务的数据结构(容器)和触发最近将超时任务的机制 

        定时器应用
  •         心跳机制
  •         倒计时
  •         延时任务

容器

        对任务触发时间进行排序
  •         红黑树 STL(map/set/multimap/multiset)
  •         最小堆(priority_queue)
        对执行顺序进行排序
  •         时间轮    针对当前时间指针做偏移

触发机制

        io多路复用的最后一个超时参数

        eg:epoll_wait 第四个参数 超时参数

        如果没有定时器,就会一直等待,直到有延迟任务的出现

         

       timerfd (将定时器转化为io处理)

        timerfd_create ( int clockid, int flags) ;               创建定时器文件描述符

        clockid:

        CLOCK_REALTIME  系统实时时钟,与系统时间同步,受用户手动修改系统时间影响

        CLOCK_MONOTONIC  单调递增时钟,与系统启动开始计算,不受系统时间修改影响

        flags:

        TFD_NONBLOCK  非阻塞模式,若定时器未到期,read 立刻返回 EAGAIN 错误

        timerfd_sertime(int fd, int flags, const struct itimerspec *new_value,struct itimerspec * old_value);                启动或停止定时器,设置超时时间和间隔

        flags: 0 相对时间模式,new_value.it_value 表示相对于当前时间的延迟

                    TFD_TIMER_ABSTIME 绝对时间模式,new_value.it_value 表示具体时间点(如CLODK_REALTIME 的某个时间戳)        

        timerfd 使用:

        1.ET模式

        2.monotonic  时间参数

 定时器优化思路

        思考:

        1.选择数据结构
  •         触发时刻作为 key ,任务作为 val
  •         快速找到最近要超时的任务   O(1)
  •         触发后要删除该任务且支持随时删除任务,还要满足快速找到任务
  •         允许相同时刻触发任务

        因为有key-val,还要支持O(1)的查找,所以我们能想到 map,但是同一时刻可能会有多个任务,所以 我们使用 multimap作为容器。

        2.设计任务

        因为是异步执行延时任务,所以我们用回调函数,并使用函数对象

        

         3.如何触发

        这里我们使用epoll_wait 第四个参数  超时参数

         性能优化

        1.针对 不同功能或不同对象  设置定时器

        2.针对多线程进行优化: 一个事件一个定时器, 或 单独时间轮线程

        3.针对有规律的定时器任务(有大量相同时间间隔时间的延时任务),采用 emplace_hint接口

代码实现 

class TimerNode {
public:
    friend class Timer;
    TimerNode(uint64_t timeout, std::function<void()> callback)
        : timeout_(timeout), callback_(std::move(callback)) {}
private:
    int id;
    uint64_t timeout_;
    std::function<void()> callback_;
};

class Timer {
public:
    static Timer* GetInstance() {
        static Timer instance;
        return &instance;
    }

    static uint64_t GetCurrentTime() {
        using namespace std::chrono;
        return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
    }

    TimerNode* AddTimeout(uint64_t diff, std::function<void()> cb) {
        auto node  =  new TimerNode(GetCurrentTime() + diff, std::move(cb));
        if (timer_map_.empty() || node->timeout_ < timer_map_.rbegin()->first) {
            auto it = timer_map_.insert(std::make_pair(node->timeout_, std::move(node)));
            return it->second;
        } else {
            auto it = timer_map_.emplace_hint(timer_map_.crbegin().base(), std::make_pair(node->timeout_, std::move(node)));
            return it->second;
        }
    };

    void DelTimeout(TimerNode* node) {
        auto it = timer_map_.equal_range(node->timeout_);
        for (auto iter = it.first; iter != it.second; ++iter) {
            if (iter->second == node) {
                timer_map_.erase(iter);
                break;
            }
        }
    }

    // 
    int WaitTime() {
        auto iter = timer_map_.begin();
        if (iter == timer_map_.end()) {
            return -1;
        }
        uint64_t diff = iter->first - GetCurrentTime();
        return diff > 0 ? diff : 0;
    }

    void HandleTimeout() {
        auto iter = timer_map_.begin();
        while (iter != timer_map_.end() && iter->first <= GetCurrentTime()) {
            iter->second->callback_();
            iter = timer_map_.erase(iter); // 删除已处理的定时器
        }
    }
private:
    std::multimap<uint64_t, TimerNode*> timer_map_;

    Timer() = default;
    Timer(const Timer&) = delete;
    Timer& operator=(const Timer&) = delete;
    Timer(Timer&&) = delete;
    Timer& operator=(Timer&&) = delete;
    ~Timer() {
        for (auto& pair : timer_map_) {
            delete pair.second;
        }
    }
};

学习分享:https://github.com/0voice  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值