最近有个大牛给我教了时间轮算法,并且看了他实现的源码,认为自己已经理解了他所实现的思想,这里将我的代码和理解分享一下:
时间轮是模仿钟表的方式,秒针走一圈,分针走一格,只有最里圈的任务需要执行,转一圈后分针(外一层)指向下一格,将那一格的节点重新插入,那么就会插入到秒针(最里圈)。

以下方式还有一些不足,我会再做修改:

#ifndef __TIMEWHEEL_H__
#define __TIMEWHEEL_H__
#include "templock.h"//锁的实现文件
#include <time.h>
#include <map>
#include <list>
#define SLOT_LEVEL 4 //时间圈的层数
#define TIME_NEAR  8
#define TIME_LEVEL  6
#define TIME_NEAR_SLOT  (1 << TIME_NEAR) //最里圈执行圈的槽数 1 0000 0000
#define TIME_LEVEL_SLOT  (1 << TIME_LEVEL) //除最里圈外的每圈的桶数 0100 0000
#define TIME_NEAR_MASK  ((1 << TIME_NEAR) - 1) //判断任务是否移动到最里圈的掩码 1111 1111
#define TIME_LEVEL_MASK  ((1 << TIME_LEVEL) - 1) //判断任务放在哪一圈的掩码 0011 1111
// 精度
#define PRECISION  10typedef void(*TimeOutCb)(void*);
typedef void(*CancelCb)(void*);
typedef struct  TimerNode
{unsigned int expireTime;//过期时间TimeOutCb timeOutCb; // 时间节点的回调处理函数CancelCb cancelCb; // 取消删除时间节点void* arg;  // 回调函数的参数int timeNodeId;//定时任务节点的ID,用来删除节点bool isNear; // 是否处在最近int rowNO; // 行,所处圈数int colNO;// 列,所在位置
};
class TimeWheel
{public:TimeWheel(void);
public:~TimeWheel(void);
private:std::list<TimerNode*> nearTimerSlot[TIME_NEAR_SLOT]; //最里面的时间轮槽数,总共256个std::list<TimerNode*> levelTimerSlot[SLOT_LEVEL][TIME_LEVEL_SLOT];  // 后面总共4层,每层都是64个槽unsigned int tick;      // 当前的tickunsigned int startTime; // 开始时间unsigned int currentTime;   //当前时间
private:TempLock timeLock;int timeNodeId; // 时间点的ID号std::map<int, TimerNode*> timeNodeMap; //定时任务节点ID和地址的映射volatile bool stopFlag; //是否停止
public:int  setTimer(unsigned int  milliSeconds, TimeOutCb cb, void* arg, CancelCb cancelCb = NULL);void cancelTimer(int timeNodeId);
private:int  setTimerInnerSafe(unsigned int  milliSeconds, TimeOutCb cb, void* arg, CancelCb cancelCb);void cancelTimerInnerSafe(int timeNodeId);void startTimer();void stopTimer(); // 停止void updateTimer();void deleteNode(TimerNode* timeNode, bool isTimeOut = true);void executeExpireTimeNodeSafe();void InsertTimeNode(TimerNode* newTimeNode);void moveTimeList();void moveTimeNodeSafe(std::list<TimerNode*>& nodeList);TimerNode* createTimeNode(unsigned int  milliSeconds, TimeOutCb timeOutCb, void* arg, CancelCb cancelCb);private:static void runTimer(void* arg);
private:static int gettimeofday(struct timeval* tv, struct timezone* tz);static void InitHighResAbsoluteTime();static long getCentiSeconds();
};#endif
#pragma once
#include <windows.h>
#include <process.h>
#include "TimeWheel.h"
#include <stdio.h>#define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
static VOID(WINAPI *fnGetSystemTimePreciseAsFileTime)(LPFILETIME) = NULL;
struct timezone {int  tz_minuteswest; /* minutes W of Greenwich */int  tz_dsttime;     /* type of dst correction */
};TimeWheel::TimeWheel(void):stopFlag(false)
{startTimer();//构造时就开始轮转时间
}TimeWheel::~TimeWheel(void)
{stopFlag = true;
}void TimeWheel::runTimer(void* arg){TimeWheel* timer = (TimeWheel*)arg;timer->updateTimer();
}
// 初始化所有的数组
void  TimeWheel::startTimer(){timeNodeId = 0;tick = 0;startTime = getCentiSeconds();// 记录开始时间currentTime = startTime; // 记录当前时间_beginthread(runTimer,0,this); //开启一个线程来执行任务检测和执行
}
void TimeWheel::stopTimer() { // 停止stopFlag = true;
}
void TimeWheel::deleteNode(TimerNode* timeNode,bool isTimeOut){if (timeNode->isNear)//判断是否在要执行的圈{nearTimerSlot[timeNode->colNO].remove(timeNode);//对应槽的队列删除这个节点} else {levelTimerSlot[timeNode->rowNO][timeNode->colNO].remove(timeNode);//对应层的对应槽删除这个节点}timeNodeMap.erase(timeNode->timeNodeId); // 从map中删除这个映射delete timeNode;
}void TimeWheel::executeExpireTimeNodeSafe() {timeLock.lock();unsigned int triggeSlot = tick & TIME_NEAR_MASK;//判断当前时间在哪个槽,&11111111std::list<TimerNode*> nodeList = nearTimerSlot[triggeSlot];//取出槽上挂的任务列表nearTimerSlot[triggeSlot].clear();//然后清空该槽上的任务列表,定时任务只执行一次timeLock.unlock();for (auto it = nodeList.begin(); it != nodeList.end();++it)//遍历任务列表{TimerNode* curNode = *it;curNode->timeOutCb(curNode->arg); // 调用回调函数,这里监控的问题timeLock.lock();timeNodeMap.erase(curNode->timeNodeId); // 删除Map中的世界节点,执行过后就不需要记录了timeLock.unlock();delete curNode;//删除该时间节点}
}
void TimeWheel::moveTimeNodeSafe(std::list<TimerNode*>& nodeList ){timeLock.lock();for (auto it = nodeList.begin(); it != nodeList.end();)//遍历槽上的定时任务,重新插入到时间轮中{TimerNode*  moveNode = *it;InsertTimeNode(moveNode); // 重新插入it = nodeList.erase(it++);//将原位置的任务删掉}timeLock.unlock();
}
void TimeWheel::moveTimeList(){unsigned int curTick = ++tick;//当前时间等于tick+1if (curTick == 0){return;}unsigned int mask = 1 << TIME_NEAR; //1 0000 0000unsigned int time = curTick >> TIME_NEAR; // 时间右移8位,// 等于0 表示进位int i = 0;//如果当前时间&上掩码等于0, 那么轮转弯1圈了,需要移动任务列表while((curTick & (mask - 1)/*1111 1111*/) == 0) {       int moveSlot = time & TIME_LEVEL_MASK; //时间&0011 1111,移动的槽的位置if (moveSlot != 0) // 最小为1,{moveTimeNodeSafe(levelTimerSlot[i][moveSlot]);levelTimerSlot[i][moveSlot].clear();break;}time >>= TIME_LEVEL;//时间右移6位,判断下一层mask <<= TIME_LEVEL;//掩码左移6位,判断是哪一圈++i;//层数加1}
}
void TimeWheel::updateTimer(){while (!stopFlag)//判断该定时器是否结束生命周期{int curTime = getCentiSeconds();//获取当前时间int timeVal = curTime - currentTime;//获取当前时间和刚才的时间差currentTime = curTime;//然后记录当前时间for (int i = 0; i < timeVal; i++) //循环时间差的次数{executeExpireTimeNodeSafe();//先执行一次,检查0位槽是否有要执行的任务moveTimeList();//移动时间队列executeExpireTimeNodeSafe();//执行槽上的任务}Sleep(PRECISION);//沉睡10ms,精度差异}
}
//初始化时间节点
TimerNode* TimeWheel::createTimeNode(unsigned int  milliSeconds,TimeOutCb timeOutCb,void* arg,CancelCb cancelCb){TimerNode* newTimeNode = new TimerNode;memset(newTimeNode,0,sizeof(TimerNode));newTimeNode->arg = arg;newTimeNode->expireTime = tick + milliSeconds;newTimeNode->timeOutCb = timeOutCb;newTimeNode->cancelCb = cancelCb;return newTimeNode;
}
//主要使用的方法,用来执行定时任务
int  TimeWheel::setTimer(unsigned int  milliSeconds,TimeOutCb cb,void* arg,CancelCb cancelCb){return setTimerInnerSafe(milliSeconds,cb,arg,cancelCb);
}
//根据定时任务的ID从列表和map中删除他
void  TimeWheel::cancelTimer(int timeNodeId) {cancelTimerInnerSafe(timeNodeId);
}
int  TimeWheel::setTimerInnerSafe(unsigned int  milliSeconds,TimeOutCb cb,void* arg,CancelCb cancelCb) {timeLock.lock();//printf("设置----\n");milliSeconds /= PRECISION; // 精度TimerNode*  newTimeNode = createTimeNode(milliSeconds,cb,arg,cancelCb);newTimeNode->timeNodeId = ++timeNodeId;timeNodeMap[newTimeNode->timeNodeId] = newTimeNode; // 放到map里面InsertTimeNode(newTimeNode);//插入新的时间节点timeLock.unlock();return newTimeNode->timeNodeId;
}
void TimeWheel::cancelTimerInnerSafe(int timeNodeId) {timeLock.lock();    TimerNode* timeNode = timeNodeMap[timeNodeId];//根据ID查找到节点地址if (timeNode == NULL)//如果节点不存在,那么就返回{timeLock.unlock();return;}// 执行这个节点取消的回调if (timeNode->cancelCb != NULL){timeNode->cancelCb(timeNode->arg);  }timeNodeMap.erase(timeNodeId);//map中删除这个映射deleteNode(timeNode);//删除这个节点timeLock.unlock();
}
void  TimeWheel::InsertTimeNode(TimerNode* newTimeNode){unsigned int expireTime = newTimeNode->expireTime; //到期时间unsigned int milliSeconds = newTimeNode->expireTime - tick; // 剩余多少时间的Tickif ((milliSeconds | TIME_NEAR_MASK) == TIME_NEAR_MASK)//判断是否已经到快要执行的时间了{int insertSlot = expireTime & TIME_NEAR_MASK; //计算插入到最里圈255个槽的哪个槽newTimeNode->isNear = true; //将即将执行设为1newTimeNode->colNO = insertSlot; //列设为槽数nearTimerSlot[insertSlot].push_back(newTimeNode);//放入即将执行的位置} else {//如果不是即将执行的,不在255个槽的范围内,那么就要判断放在哪个位置unsigned int mask = (1 << TIME_NEAR) << TIME_LEVEL; //掩码是 100 0000 0000 0000,后8位是最里圈的范围,之前的是层数范围int i = 0;for (i = 0; i < SLOT_LEVEL; i++) //不会超过最大层数{if ((milliSeconds | (mask - 1))  == (mask - 1)) //如果当前时间或上11 1111 1111 1111还等于11 1111 1111 1111的话,那么它就在这个范围内,就是这层没错{break;}mask = mask << TIME_LEVEL;//掩码左移6位,就是下一层,然后再判断是否在下一层的位置}int insertSlot = (expireTime >> (TIME_NEAR + i * TIME_LEVEL)) & (mask - 1); // 计算这一层应该插入哪个槽newTimeNode->isNear = false;//不在执行圈newTimeNode->rowNO = i;//层数newTimeNode->colNO = insertSlot;//槽数levelTimerSlot[i][insertSlot].push_back(newTimeNode);//插到应该的位置}
}
// 1秒 = 1000 毫秒 = 1000,000 微秒
// 获取百分之一秒
long TimeWheel::getCentiSeconds() {struct timeval tv;gettimeofday(&tv,NULL);return tv.tv_sec * 1000 / PRECISION  + tv.tv_usec / (1000 * PRECISION);
}//****************************工具函数*******************************
int  TimeWheel::gettimeofday(struct timeval* tv,struct timezone* tz){FILETIME ft;unsigned __int64 tmpres = 0;static int tzflag;if (NULL == fnGetSystemTimePreciseAsFileTime) {InitHighResAbsoluteTime();}if (NULL != tv) {fnGetSystemTimePreciseAsFileTime(&ft);tmpres |= ft.dwHighDateTime;tmpres <<= 32;tmpres |= ft.dwLowDateTime;/*converting file time to unix epoch*/tmpres /= 10;  /*convert into microseconds*/tmpres -= DELTA_EPOCH_IN_MICROSECS;tv->tv_sec = (long) (tmpres / 1000000UL);tv->tv_usec = (long) (tmpres % 1000000UL);}if (NULL != tz) {if (!tzflag) {_tzset();tzflag++;}tz->tz_minuteswest = _timezone / 60;tz->tz_dsttime = _daylight;}return 0;
}void  TimeWheel::InitHighResAbsoluteTime() {FARPROC fp;HMODULE module;if (fnGetSystemTimePreciseAsFileTime != NULL)return;fnGetSystemTimePreciseAsFileTime = GetSystemTimeAsFileTime;module = GetModuleHandleA("kernel32.dll");if (module) {fp = GetProcAddress(module, "GetSystemTimePreciseAsFileTime");if (fp) {fnGetSystemTimePreciseAsFileTime = (VOID(WINAPI*)(LPFILETIME)) fp;}}
}

使用方式:

TimeWheel tiemer;
timer.settime(300,CB,void*arg,cancelCB);
//然后就等待执行拉

时间轮定时器-Timewheel相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. 时间轮(TimeWheel)的设计与实现

    一.前言 由于工作的需要,得实现一个用于控制事件超时抛弃的时间轮,由于这是一个相对独立的接口,就总结分享一下. 首先看下需求,此时间轮需要具备下面几个功能: 1)能添加事件,同时附上其超时时间: 2) ...

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

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

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

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

最新文章

  1. 【Spring】spring基于纯注解的声明式事务控制
  2. git命令从入门到实战,看这一篇足矣
  3. cordova ios的问题
  4. nginx0.8 + php-5.3.4 + memcached
  5. 原:开辟内存时,可以以结构体为单位,这样测试,是对的吧?
  6. Firefly官方教程之Netconnect使用文档
  7. json带斜杠java,带斜杠的json解析
  8. Excel有哪些需要熟练掌握而很多人不会的技能!
  9. AJAX入门——工作原理
  10. Spark常见问题解决办法
  11. java 不让滚动条随着拖拽滑动_「最近项目小结」使用Vue实现一个简单的鼠标拖拽滚动效果插件...
  12. 台大李宏毅Machine Learning 2017Fall学习笔记 (16)Unsupervised Learning:Neighbor Embedding
  13. 时间控件(选择时间范围的插件)
  14. 中国脑计划颠覆性创新之路二,欧美脑计划存在重大缺陷
  15. 【Python】PDF转图片
  16. JMeter 如何生成指定取值范围、指定小数位数的随机浮点数
  17. 30天自制操作系统——自写设计
  18. jpeg 与 png 图片格式的区别
  19. HTML5七夕情人节表白网页❤抖音超火的樱花雨3D相册❤ HTML+CSS+JavaScript
  20. 怎么写出一本程序员风格的修真小说

热门文章

  1. 使用ffmpeg下载m3u8流媒体
  2. jeston nano usb转485通信,usb扩展连接多个设备设置固定
  3. 计算时间间隔 日历牌上的 周数
  4. MySQL索引优化的几种方式
  5. 微信H5保存分享图片
  6. sigmoid 激励函数
  7. base64加密原理
  8. 基于微信小程序的竞赛管理系统
  9. 时延波束形成 matlab,有关波束形成程序解释
  10. 全新ThihkPHP聚合支付系统源码+兼容全部易支付