时间轮

老早之前就听说时间轮算法特别高效,Linux内核都用的它,这两天抽空实现了遍……嗯,被差一bug搞死(~ ̄▽ ̄~) 啊哈

网上扣来的图,原理好懂:轮子里的每格代表一小段时间(精度),连起来就能表示时间点了(我去年买了个表),格子内含链表,中存回调函数;时间指针每次转动一格,指向某格时,取出链表里的回调函数依次执行,后清空链表,等待下一次转动。

加入节点逻辑也简单:在轮子可表示的时间范围内(格子数*格子精度),配合当前时针位置,对格子总数取余,即得本节点需放哪个格子。

进一步为扩大时间轮的表示范围,使用分级方式,跟时分秒一样,上一级转一圈,下一级动一格。

 

对吧,很容易理解……然后coding是另一码事(╯‵□′)╯︵┴─┴

首先,最重要的,数据结构:用了环形链表,方便增删。

struct NodeLink {NodeLink* prev;NodeLink* next;NodeLink() { prev = next = this; } //circle
};
struct stWheel {NodeLink* slots; //每个slot维护的node链表为一个环,slot->next为node链表中第一个节点,prev为node的最后一个节点const uint32 size;uint32 slotIdx;stWheel(uint32 n) : size(n), slotIdx(0){ slots = new NodeLink[size]; }~stWheel() {if (slots) {for (uint32 j = 0; j < size; ++j) {NodeLink* link = (slots + j)->next;while (link != slots + j) {TimerNode* node = (TimerNode*)link;link = node->link.next;delete node;}}delete[]slots;}}
};

具体时间节点的数据结构如下:

struct TimerNode {Pool_Obj_Define(TimerNode, 32) //内存池声明,不含数据NodeLink link; //must in the head
    uint32 timeDead;uint32 interval; //间隔多久int loop;        //总共循环多久std::function<void()> func;
};

TimeNode里保存上下关系,stWheel的NodeLink辅助用的,环状链表的头,没实际数据,用以记录首尾TimeNode。

核心代码如下:

——增删节点——

void CTimerMgr::_AddTimerNode(uint32 milseconds, TimerNode* node) {NodeLink* slot = NULL;uint32 tickCnt = milseconds / TIME_TICK_LEN;if (tickCnt < WHEEL_CAP[0]) {uint32 index = (_wheels[0]->slotIdx + tickCnt) & (WHEEL_SIZE[0] - 1); //2的N次幂位操作取余slot = _wheels[0]->slots + index;} else {for (int i = 1; i < WHEEL_NUM; ++i) {if (tickCnt < WHEEL_CAP[i]) {uint32 preCap = WHEEL_CAP[i - 1]; //上一级总容量即为本级的一格容量uint32 index = (_wheels[i]->slotIdx + tickCnt / preCap - 1) & (WHEEL_SIZE[i] - 1); //勿忘-1slot = _wheels[i]->slots + index;break;}}}NodeLink* link = &(node->link);link->prev = slot->prev; //插入格子的prev位置(尾节点)link->prev->next = link;link->next = slot;slot->prev = link;
}
void CTimerMgr::RemoveTimer(TimerNode* node) {LOG_TRACK("node[%p], timeDead[%lld]", node, node->timeDead);NodeLink* link = &(node->link);if (link->prev) {link->prev->next = link->next;}if (link->next) {link->next->prev = link->prev;}link->prev = link->next = NULL;delete node;
}

——轮子启动——

void CTimerMgr::CheckTimerList(const uint32 timenow) {uint32 tickCnt = timenow > _checkTime ? (timenow - _checkTime) / TIME_TICK_LEN : 0;//if (tickCnt) Printf();for (uint32 i = 0; i < tickCnt; ++i) { //扫过的slot均超时stWheel* wheel = _wheels[0];NodeLink* slot = wheel->slots + wheel->slotIdx;NodeLink* link = slot->next;slot->next = slot->prev = slot; //清空当前格子while (link != slot) {            //环形链表遍历TimerNode* node = (TimerNode*)link;link = node->link.next; //得放在前面,后续函数调用,可能会更改node的链接关系
            AddToReadyNode(node);}if (++(wheel->slotIdx) >= wheel->size) {wheel->slotIdx = 0;Cascade(1, timenow); //跳级
        }_checkTime += TIME_TICK_LEN;}DoTimeOutCallBack();
}
uint32 CTimerMgr::Cascade(uint32 wheelIdx, const uint32 timenow) {if (wheelIdx < 1 || wheelIdx >= WHEEL_NUM) {return 0;}int casCnt = 0;stWheel* wheel = _wheels[wheelIdx];NodeLink* slot = wheel->slots + wheel->slotIdx;NodeLink* link = slot->next;slot->next = slot->prev = slot; //清空当前格子while (link != slot) {TimerNode* node = (TimerNode*)link;link = node->link.next;if (node->timeDead <= timenow) {AddToReadyNode(node);} else {_AddTimerNode(node->timeDead - timenow, node); //本级精度下已超时,精度提升,重新加一遍++casCnt;LOG_TRACK("wheelIdx[%u], link[%p], milseconds[%u]", wheelIdx, link, node->timeDead - timenow);}}if (++(wheel->slotIdx) >= wheel->size) {wheel->slotIdx = 0;casCnt += Cascade(++wheelIdx, timenow);}return casCnt;
}

 

那么问题来了:大于,大于等于,边界,减一……搞错几多次 ○(* ̄︶ ̄*)○ 吃饱睡好

 

 

源码地址:https://github.com/3workman/Tools/tree/master/src/Timer

转载于:https://www.cnblogs.com/3workman/p/6063819.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/542233.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

qt tab弹出特效_Nuke Studio 12(影视特效合成软件)中文版分享

Nuke 12是一款功能强大&#xff0c;世界知名的影视后期特效合成软件。NUKE是一个获得学院奖(Academy Award)的数码合成软件。已经经过10年的历练&#xff0c;为艺术家们提供了创造具有高质素的相片效果的图像的方法。NUKE无需专门的硬件平台&#xff0c;但却能为艺术家提供组合…

【转】unity地形插件T4M使用帮助

unity的地形系统在手机游戏中因为效率问题基本无法使用&#xff0c;只能通过T4M这个地形插件来进行优化制作。下面大概讲解一下使用流程及方法。 先中U3D里面用自带的地形系统刷出想要的地形和贴图。贴图可以大概刷一下。后面要重新刷。 用导出脚本ExportTerrain.js导出地形为O…

Linux的简介与虚拟机的管理

Linux的简介&#xff1a; 严格的来讲&#xff0c;Linux不算是一个操作系统&#xff0c;只是一个Linux系统中的内核&#xff0c;Linux的全称是GUN/Linux&#xff0c;这才算是一个真正意义上的Linux系统。 Linux是一个多用户多任务的操作系统&#xff0c;拥有良好的用户界面&…

R语言:ggplot2精细化绘图——以实用商业化图表绘图为例(转)

本文旨在介绍R语言中ggplot2包的一些精细化操作&#xff0c;主要适用于对R画图有一定了解&#xff0c;需要更精细化作图的人&#xff0c;尤其是那些刚从excel转ggplot2的各位&#xff0c;有比较频繁的作图需求的人。不讨论那些样式非常酷炫的图表&#xff0c;以实用的商业化图表…

Linux中常用的命令

1.文件建立 touch file&#xff08;文件的名字&#xff09; 注意&#xff1a; touch不但可以建立文件也可以修改文件的时间戳 时间戳分为&#xff1a; atime&#xff1a;文件内容被访问的时间标识 mtime&#xff1a;文件内容被修改的时间标识 ctime&#xff1a;文件属性或文件内…

Linux系统中输出输入的管理

1.什么是输入和输出 输入和输出是计算机系统中的主机与外部进行通信的系统。它由外围设备和输入输出控制系统两部分组成&#xff0c;我们在shell中键入指令&#xff0c;然后送入CPU中运算产生结果&#xff0c;再将结果送到字符设备中显示。简单点来说输入输出就是通过我们的键盘…

Linux系统中用户的管理

#####用户管理###### 1在Linux中&#xff0c;有三种用户&#xff1a; 1 root : 也成为超级用户&#xff0c;对系统有控制权限&#xff0c;超级用户可以不受限制的运行任何命令&#xff0c;root 用户可以看作是系统的管理员。 2 系统用户&#xff1a; 系统用户通常为系统功能所必…

Linux中对进程的管理

1.what is 进程 程序&#xff08;program&#xff09;放置在储存媒体中&#xff08;如硬盘、光盘、软盘、磁盘等&#xff09;&#xff0c;为实体的型态存在。 进程&#xff1a;程序被触发后&#xff0c;执行者的权限与属性、程序的程序码与所需数据等都会被载入内存中&#xff…

Linux远程连接与sshd服务安全设定

1.远程连接&#xff1a; 首先设置ip&#xff1a; 设置好之后&#xff0c;先ping一下IP 看能不能通 ssh root172.25.13.103 ##表示的是&#xff1a;连接ip为172.25.13.103的root用户 2.系统控制命令 系统控制命令的查看相关参数如下表 systemctl服务控制命令systemctl stat…

一个简单的封ip规则

2019独角兽企业重金招聘Python工程师标准>>> 一个简单通过nginx日志封ip规则&#xff08;仅仅自己方便使用&#xff09; #!/bin/bash #Version:1.0 #Date:2016-08-09 #作用:防刷IP地址,解封蜘蛛,解封5天前封的IP地址function deny () { Date$(date "%F-%H-%M&q…

系统日志管理

1 查看系统中的日志 rsyslog 此服务是用来采集系统日志的&#xff0c;他不产生日志&#xff0c;只是起到采集作用 2 rsyslog 的管理 /var/log/messages服务信息日志/var/log/secuer系统登陆日志/var/log/cron定时任务日志/var/log/maillog邮件日志/var/log/boot.log系统启动日…

iOS10 UI教程视图的边界与视图的框架

2019独角兽企业重金招聘Python工程师标准>>> iOS10 UI教程视图的边界与视图的框架 iOS10 UI视图的边界 在视图的几何形状中我们提到了视图属性中的一部分属性可以将定义的视图绘制在屏幕上。其中典型的3个属性为边界属性、框架属性以及中心位置属性。 bounds表示的就…

Linux中远程文件的传输

1. scp命令 scp file userip:/dir 把自己主机的文件远程复制到其他主机 scp userip:/file dir 把其他主机的文件远程复制到当前主机 注意&#xff1a;要关闭接受端的防火墙 把主机的file远程复制到IP为172.25.254.117的root用户的Desktop 把IP为172.25.254.117的root用户Deskt…

引入antd组件样式_个人作品:一个技术栈koa2+ mysql+react + antd的个人博客

前言此项目是个人博客&#xff0c;有前端界面后台管理系统&#xff1b;目的是当做react和node的练手项目&#xff0c;同时还可以了解到服务器nginx部署web站点以及备案和域名的基本操作流程。项目预览地址https://www.lxsblog.cn​www.lxsblog.cnGitHub地址LinWeb/blog​github…

浅谈Jfinal急速开发框架

2019独角兽企业重金招聘Python工程师标准>>> 使用Jfinal一段时间了,记得当初14年吧,为了建立一个简单的门户网站,想找个轻量型的急速开发框架,然后搜到Jfinal,然后用了一段时间后,确实不错, 现在吧,随着时间的推移,作者对JFinal的版本迭代也是一直在努力,一直朝着优…

虚拟机的管理

我们采用虚拟机的原因是什么呢&#xff0c;很简单就俩字&#xff1a; 节能 1. 管理虚拟机的命令&#xff1a; virt-manager开启虚拟机管理器virsh list显示正在运行的虚拟机virsh list --all查看所有虚拟机virsh start desktop打开虚拟机virsh shutdown desktop正常关闭虚拟机…

Linux中的软件管理

1. 使用已有的网络安装资源安装软件 cd /etc/yum.repos.d/ (移动到yum源指向的文件配置目录下&#xff09; vim westos.repo &#xff08;新建文件&#xff0c;yum下后缀必须为.repo) 编辑这个文件里面写 [redhat] &#xff08;软件仓库名称&#xff09; namefirefox &#x…

android--------Popupwindow的使用

2019独角兽企业重金招聘Python工程师标准>>> PopupWindow在Android.widget包下&#xff0c;项目中经常会使用到PopupWindow做菜单选项&#xff0c; PopupWindow这个类用来实现一个弹出框&#xff0c;可以使用任意布局的View作为其内容&#xff0c;这个弹出框是悬浮…

安装虚拟机的脚本

1. 先安装生成自动安装脚本的工具 yum install system-config-kickstart -y 2. 打开这个软件 system-config-kickstart 基本设置&#xff1a;更改时区为上海&#xff0c;设置root用户密码 2&#xff09;设置安装方法为网络安装&#xff0c;将共享的镜像文件地址正确填写 3&…

小小小游戏

写着玩 FlappyBird 视频:https://pan.baidu.com/s/1sljIR5z 游戏:https://pan.baidu.com/s/1ge8j7Ej 项目:https://pan.baidu.com/s/1eSysxpw Breakout 视频:https://pan.baidu.com/s/1gfhv4hd 项目:https://pan.baidu.com/s/1hs8xPly QBert 视频:https://pan.baidu.com/s/1s…