最近实现了一个多级时间轮定时器,经过了简单的测试,如果将时钟步长设置为10ms以内误差会比较大。最新代码地址TimeWheel Github。

特性

  • C++11实现
  • 可自定义时间轮层级
  • 可自定义时间轮刻度
  • 可设置时钟步长
  • 误差基本在时钟步长以内
  • 推荐时钟步长50ms

类介绍

  • 代码由三个类组成:Timer,TimeWheel,TimeWheelScheduler。

    • Timer:定时器类,封装了定时器任务std::function<void()>
    • TimeWheel:时间轮类,每个时间轮都有自己的刻度,当低级的时间轮刻度转满一圈的时候,当前时间轮进行进位,即当前时间轮刻度加一。每个时间轮刻度可设置为N,在每个刻度都对应了一个双向链表来保存该时刻的定时器std::vector<std::list<TimerPtr>>(N)
    • TimeWheelScheduler:时间轮调度,启动一个线程按照固定的时钟步长去运行时间轮。提供创建定时器的接口CreateTimerAtCreateTimerAfterCreateTimerEvery

例子

int main() {// Four level time wheels: Hour, Minute, Secon, Millisecond.int timer_step_ms = 50;TimeWheelScheduler tws(timer_step_ms);// Hour time wheel. 24 scales, 1 scale = 60 * 60 * 1000 ms.tws.AppendTimeWheel(24, 60 * 60 * 1000, "HourTimeWheel");// Minute time wheel. 60 scales, 1 scale = 60 * 1000 ms.tws.AppendTimeWheel(60, 60 * 1000, "MinuteTimeWheel");// Second time wheel. 60 scales, 1 scale = 1000 ms.tws.AppendTimeWheel(60, 1000, "SecondTimeWheel");// Millisecond time wheel. 1000/timer_step_ms scales, 1 scale = timer_step ms.tws.AppendTimeWheel(1000 / timer_step_ms, timer_step_ms, "MillisecondTimeWheel");tws.Start();tws.CreateTimerAt(GetNowTimestamp() + 10000, []() {std::cout << "At now+10s" << std::endl;});tws.CreateTimerAfter(500, []() {std::cout << "After 0.5s" << std::endl;});std::cout << timetoStr() << std::endl;auto timer_id = tws.CreateTimerEvery(5000, []() {std::cout << "Every 5s: " << timetoStr() << std::endl;});tws.CreateTimerEvery(30 * 1000, []() {std::cout << "Every 30s: " << timetoStr() <<std::endl;});tws.CancelTimer(timer_id);std::this_thread::sleep_for(std::chrono::minutes(20));tws.Stop();return 0;
}

该例创建了一个四级的时间轮:小时级(高级时间轮),分钟级,秒级,毫秒级(低级时间轮)。

  • 小时级:以1小时为1个刻度,总共24小时则24个刻度。1小时等于60*60*1000毫秒。tws.AppendTimeWheel(24, 60 * 60 * 1000, "HourTimeWheel")

  • 分钟级:以1分钟为1个刻度,总共60分钟则60个刻度。1分钟等于60*1000毫秒。tws.AppendTimeWheel(60, 60 * 1000, "MinuteTimeWheel");

  • 秒级:以1秒为1个刻度,总共60秒则60个刻度。1秒等于1000毫秒。tws.AppendTimeWheel(60, 1000, "SecondTimeWheel");

  • 毫秒级:以1个时钟步长timer_step_ms为1个单位,1秒等于1000毫秒,那么则有1000/timer_step_ms个刻度。tws.AppendTimeWheel(1000 / timer_step_ms, timer_step_ms, "MillisecondTimeWheel");

代码

timer.h

#ifndef TIMER_H_
#define TIMER_H_#include <cstdint>
#include <functional>
#include <memory>typedef std::function<void()> TimerTask;class Timer {public:Timer(uint32_t id, int64_t when_ms, int64_t interval_ms, const TimerTask& task);void Run();uint32_t id() const {return id_;}int64_t when_ms() const {return when_ms_;}bool repeated() const {return repeated_;}void UpdateWhenTime() {when_ms_ += interval_ms_;}private:uint32_t id_;TimerTask task_;int64_t when_ms_;uint32_t interval_ms_;bool repeated_;
};using TimerPtr = std::shared_ptr<Timer>;#endif  // TIMER_H_

timer.cpp

#include "timer.h"Timer::Timer(uint32_t id, int64_t when_ms, int64_t interval_ms, const TimerTask& handler): interval_ms_(interval_ms), repeated_(interval_ms_ > 0), when_ms_(when_ms), id_(id), task_(handler) {}void Timer::Run() {if (task_) {task_();}
}

time_wheel.h

#ifndef TIME_WHEEL_H_
#define TIME_WHEEL_H_#include <chrono>
#include <string>
#include <memory>
#include <vector>
#include <list>#include "timer.h"class TimeWheel {public:TimeWheel(uint32_t scales, uint32_t scale_unit_ms, const std::string& name = "");uint32_t scale_unit_ms() const {return scale_unit_ms_;}uint32_t scales() const {return scales_;}uint32_t current_index() const {return current_index_;}void set_less_level_tw(TimeWheel* less_level_tw) {less_level_tw_ = less_level_tw;}void set_greater_level_tw(TimeWheel* greater_level_tw) {greater_level_tw_ = greater_level_tw;}int64_t GetCurrentTime() const;void AddTimer(TimerPtr timer);void Increase();std::list<TimerPtr> GetAndClearCurrentSlot();private:std::string name_;uint32_t current_index_;// A time wheel can be devided into multiple scales. A scals has N ms.uint32_t scales_;uint32_t scale_unit_ms_;// Every slot corresponds to a scale. Every slot contains the timers.std::vector<std::list<TimerPtr>> slots_;TimeWheel* less_level_tw_;  // Less scale unit.TimeWheel* greater_level_tw_;  // Greater scale unit.
};using TimeWheelPtr = std::shared_ptr<TimeWheel>;inline int64_t GetNowTimestamp() {using namespace std::chrono;auto now = system_clock::now().time_since_epoch();return duration_cast<milliseconds>(now).count();
}#endif  // TIME_WHEEL_H_

time_wheel.cpp

#include "time_wheel.h"TimeWheel::TimeWheel(uint32_t scales, uint32_t scale_unit_ms, const std::string& name): name_(name), current_index_(0), scales_(scales), scale_unit_ms_(scale_unit_ms), slots_(scales), greater_level_tw_(nullptr), less_level_tw_(nullptr) {}int64_t TimeWheel::GetCurrentTime() const {int64_t time = current_index_ * scale_unit_ms_;if (less_level_tw_ != nullptr) {time += less_level_tw_->GetCurrentTime();}return time;
}void TimeWheel::AddTimer(TimerPtr timer) {int64_t less_tw_time = 0;if (less_level_tw_ != nullptr) {less_tw_time = less_level_tw_->GetCurrentTime();}int64_t diff = timer->when_ms() + less_tw_time - GetNowTimestamp();// If the difference is greater than scale unit, the timer can be added into the current time wheel.if (diff >= scale_unit_ms_) {size_t n = (current_index_ + diff / scale_unit_ms_) % scales_;slots_[n].push_back(timer);return;}// If the difference is less than scale uint, the timer should be added into less level time wheel.if (less_level_tw_ != nullptr) {less_level_tw_->AddTimer(timer);return;}// If the current time wheel is the least level, the timer can be added into the current time wheel.slots_[current_index_].push_back(timer);
}void TimeWheel::Increase() {// Increase the time wheel.++current_index_;if (current_index_ < scales_) {return;}// If the time wheel is full, the greater level time wheel should be increased.// The timers in the current slot of the greater level time wheel should be moved into// the less level time wheel.current_index_ = current_index_ % scales_;if (greater_level_tw_ != nullptr) {greater_level_tw_->Increase();std::list<TimerPtr> slot = std::move(greater_level_tw_->GetAndClearCurrentSlot());for (TimerPtr timer : slot) {AddTimer(timer);}}
}std::list<TimerPtr> TimeWheel::GetAndClearCurrentSlot() {std::list<TimerPtr> slot;slot = std::move(slots_[current_index_]);return slot;
}

time_wheel_scheduler.h

#ifndef TIME_WHEEL_SCHEDULER_H_
#define TIME_WHEEL_SCHEDULER_H_#include <mutex>
#include <vector>
#include <thread>
#include <unordered_set>#include "time_wheel.h"class TimeWheelScheduler {public:explicit TimeWheelScheduler(uint32_t timer_step_ms = 50);// Return timer id. Return 0 if the timer creation fails.uint32_t CreateTimerAt(int64_t when_ms, const TimerTask& handler);uint32_t CreateTimerAfter(int64_t delay_ms, const TimerTask& handler);uint32_t CreateTimerEvery(int64_t interval_ms, const TimerTask& handler);void CancelTimer(uint32_t timer_id);bool Start();void Stop();void AppendTimeWheel(uint32_t scales, uint32_t scale_unit_ms, const std::string& name = "");private:void Run();TimeWheelPtr GetGreatestTimeWheel();TimeWheelPtr GetLeastTimeWheel();private:std::mutex mutex_;std::thread thread_;bool stop_;std::unordered_set<uint32_t> cancel_timer_ids_;uint32_t timer_step_ms_;std::vector<TimeWheelPtr> time_wheels_;
};#endif  // TIME_WHEEL_SCHEDULER_H_

time_wheel_scheduler.cpp

#include "time_wheel_scheduler.h"static uint32_t s_inc_id = 1;TimeWheelScheduler::TimeWheelScheduler(uint32_t timer_step_ms): timer_step_ms_(timer_step_ms), stop_(false) {}bool TimeWheelScheduler::Start() {if (timer_step_ms_ < 50) {return false;}if (time_wheels_.empty()) {return false;}thread_ = std::thread(std::bind(&TimeWheelScheduler::Run, this));return true;
}void TimeWheelScheduler::Run() {while (true) {std::this_thread::sleep_for(std::chrono::milliseconds(timer_step_ms_));std::lock_guard<std::mutex> lock(mutex_);if (stop_) {break;}TimeWheelPtr least_time_wheel = GetLeastTimeWheel();least_time_wheel->Increase();std::list<TimerPtr> slot = std::move(least_time_wheel->GetAndClearCurrentSlot());for (const TimerPtr& timer : slot) {auto it = cancel_timer_ids_.find(timer->id());if (it != cancel_timer_ids_.end()) {cancel_timer_ids_.erase(it);continue;}timer->Run();if (timer->repeated()) {timer->UpdateWhenTime();GetGreatestTimeWheel()->AddTimer(timer);}}}
}void TimeWheelScheduler::Stop() {{std::lock_guard<std::mutex> lock(mutex_);stop_ = true;}thread_.join();
}TimeWheelPtr TimeWheelScheduler::GetGreatestTimeWheel() {if (time_wheels_.empty()) {return TimeWheelPtr();}return time_wheels_.front();
}TimeWheelPtr TimeWheelScheduler::GetLeastTimeWheel() {if (time_wheels_.empty()) {return TimeWheelPtr();}return time_wheels_.back();
}void TimeWheelScheduler::AppendTimeWheel(uint32_t scales, uint32_t scale_unit_ms, const std::string& name) {TimeWheelPtr time_wheel = std::make_shared<TimeWheel>(scales, scale_unit_ms, name);if (time_wheels_.empty()) {time_wheels_.push_back(time_wheel);return;}TimeWheelPtr greater_time_wheel = time_wheels_.back();greater_time_wheel->set_less_level_tw(time_wheel.get());time_wheel->set_greater_level_tw(greater_time_wheel.get());time_wheels_.push_back(time_wheel);
}uint32_t TimeWheelScheduler::CreateTimerAt(int64_t when_ms, const TimerTask& handler) {if (time_wheels_.empty()) {return 0;}std::lock_guard<std::mutex> lock(mutex_);++s_inc_id;GetGreatestTimeWheel()->AddTimer(std::make_shared<Timer>(s_inc_id, when_ms, 0, handler));return s_inc_id;
}uint32_t TimeWheelScheduler::CreateTimerAfter(int64_t delay_ms, const TimerTask& handler) {int64_t when = GetNowTimestamp() + delay_ms;return CreateTimerAt(when, handler);
}uint32_t TimeWheelScheduler::CreateTimerEvery(int64_t interval_ms, const TimerTask& handler) {if (time_wheels_.empty()) {return 0;}std::lock_guard<std::mutex> lock(mutex_);++s_inc_id;int64_t when = GetNowTimestamp() + interval_ms;GetGreatestTimeWheel()->AddTimer(std::make_shared<Timer>(s_inc_id, when, interval_ms, handler));return s_inc_id;
}void TimeWheelScheduler::CancelTimer(uint32_t timer_id) {std::lock_guard<std::mutex> lock(mutex_);cancel_timer_ids_.insert(timer_id);
}

C++实现多级时间轮定时器相关推荐

  1. linux编程之经典多级时间轮定时器(C语言版)

    一. 多级时间轮实现框架 上图是5个时间轮级联的效果图.中间的大轮是工作轮,只有在它上的任务才会被执行:其他轮上的任务时间到后迁移到下一级轮上,他们最终都会迁移到工作轮上而被调度执行. 多级时间轮的原 ...

  2. 高性能定时器--时间轮/多级时间轮

    运行原理 指针指向轮子上的一个槽,轮子以恒定的速度顺时针转动,每转动一步就指向下一个槽(虚线指针指向的槽),每次转动称为一个tick,一个tick的时间称为时间轮的槽间隔slot interval,即 ...

  3. 打印当前时间 毫秒_时间轮定时器

    时间轮算法(Timing-Wheel)很早出现在linux kernel 2.6中.因效率非常高,很多应用框架都实现了这个算法.还有些定时器使用最小堆实现,但总体来说,时间轮算法在插入性能上更高. 前 ...

  4. 分级时间轮优化普通时间轮定时器(2):滴答式分层计时轮

    <实现较低的计时器粒度以重传TCP(RTO):时间轮算法如何减少开销> <分级时间轮优化普通时间轮定时器> Table of Contents 描述 新闻 用法 执照 资源 其 ...

  5. 单片机定时器之改良版:时间轮定时器

    前段时间把自己以前用的单片机定时器整理出来,我称之为简单定时器,这种简单定时器比较适合定时器使用量少的程序中,如果定时器数量要求多,精度要求高,效率就会有问题,为此,俺就实现了一个时间轮定时器,简单测 ...

  6. java实现时间轮定时器_c++ 时间轮定时器实现

    前言 之所以写这篇文章,是在一篇博客中看到了时间轮定时器这个东西,感觉很是惊艳,https://www.cnblogs.com/zhongwencool/p/timing_wheel.html.在以前 ...

  7. 时间轮python_算法 数据结构——时间轮定时器

    时间轮定时器 优点:可保证每次执行定时器任务都是O(1)复杂度,在定时器任务密集的情况下,性能优势非常明显. 缺点:内存占用较大,当定时器使用不频繁,处理时间跨度很大的时候,效率低下. C++实现: ...

  8. 分级时间轮优化普通时间轮定时器

    <实现较低的计时器粒度以重传TCP(RTO):时间轮算法如何减少开销> Table of Contents Ratas-分级计时器轮 (分级)计时器轮 单文件实现,无依赖性 限制单个时间步 ...

  9. 基于libco的c++协程实现(时间轮定时器)

    在后端的开发中,定时器有很广泛的应用. 比如: 心跳检测 倒计时 游戏开发的技能冷却 redis的键值的有效期等等,都会使用到定时器. 定时器的实现数据结构选择 红黑树 对于增删查,时间复杂度为O(l ...

  10. Linux服务器编程--升序链表定时器和时间轮定时器的比较

    1 两种机制的比较 2 代码实现 两种机制的原理不赘述了,代码中有详细注释. 2.1 升序链表法 完整代码参见:https://github.com/GaoZiqiang/Cplus_daily_tr ...

最新文章

  1. Fib(兔子问题)python实现多种方法
  2. C++ 播放音频流(PCM裸流)
  3. 上传代码到gitee操作
  4. 机器学习-过拟合和欠拟合以及正则化处理
  5. C++ Primer 5th笔记(chap 10)泛型算法 :特定容器算法list
  6. Autofac框架初识与应用
  7. spring roo_使用Spring Roo进行快速云开发–第2部分:VMware Cloud Foundry
  8. EasyMock 简介
  9. Linux内核启动过程和Bootloader(总述)
  10. 敏捷开发一千零一问系列之十七:长期受制于强势客户怎么办?(上)
  11. pytorch自动求导-07
  12. onCreateView中加载大位图
  13. 无盘服务器易乐游,网维大师、易乐游无盘万兆性能评测
  14. 阿里云对象存储上传或复制文件时报The request signature we calculated does not match the signature you provided...
  15. Ajax读取本地json文件
  16. UG工程图模板制作方法,超级实用
  17. 关于yolov5训练大量数据存在的问题记录
  18. java freemarker转PDF和Word
  19. 7-35 英文字母的大小写转换
  20. [RelativeNAS] Relative Neural Architecture Search via Slow-Fast Learning

热门文章

  1. 这些基础的C语言选择题,不知道你能不能拿下
  2. Opmanager研究笔记
  3. office+visio2016版本一同安装说明
  4. Visual Studio 2017 Enterprise绿色精简版介绍
  5. 视频录制工具OBS选择区域录制
  6. 威纶触摸屏与电脑连接_威纶触摸屏与三菱PLC通信接线方法
  7. python bin文件读写_Python读写文件
  8. 谷歌浏览器html5插件怎么设置,谷歌(Chrome)浏览器插件开发教程
  9. vue json对象转数组
  10. Principle 5.13 完美汉化版 Mac平台交互动效设计神器