高性能定时器时间轮的实现

article/2025/7/28 1:08:03

时间轮的概念 

   关于定时器有很多种,有基于升序的定时器时间链表,但是这种链表存在效率的不足,就是当插入定时器的时候时间复杂度是O(n).今天,我们来认识一下高性能定时器时间轮。

   如上图所示,就是一个时间轮的基本轮廓。一个轮子上有很多槽slot,每一个槽指向一个定时器链表,这个链表是无序的。时间轮每转动一步就会指向下一个槽,其实也可以理解为一个滴答时间成为时间轮的槽间隔si (slot interval)。它实际上就是心跳时间。如果该时间轮有n个槽,因此它运转一周的时间是n*si.

   如果现在指针指向槽cs,我们此时要添加一个定时器时间为ti的定时器,则该定时器将被插入槽ts对应的链表

                                ts = (cs+ti/si)%N

   基于排序链表的定时器使用唯一的链表来管理所有定时器,所以插入定时器的数目越多,效率就会越低,而时间轮则是利用哈希表的思想,将定时器散列到不同的链表中,这样每条链表上的数据就会显著少于原来排序链表的数目。插入操作的效率基本不受定时器数目的影响(不需要排序,直接散列到一个链表上)

显然要提高时间轮的精度,就要使si(slot interval)足够小,要提高其执行效率则要N要足够大。


时间轮





时间轮的代码示例

下面我们来通过实例代码进行系统讲解时间轮

 本示例代码已经正确在机器上运行,结果得到验证!

验证运行环境:

Distributor ID: Ubuntu
Description: Ubuntu 13.04
Release: 13.04
Codename: raring

 下面是time_wheel_timer.h的源代码 


#ifndef TIME_WHEEL_TIMER
#define TIME_WHEEL_TIMER
#include<time.h>
#include<netinet/in.h>
#define BUFFER_SIZE 64
class tw_timer;
struct client_data
{sockaddr_in address;int sockfd;char buf[BUFFER_SIZE];tw_timer* timer;} ;class tw_timer
{public:tw_timer(int rot,int ts):rotation(rot),time_slot(ts),next(NULL),prev(NULL){}public:int oldrotation;	int rotation;/*记录定时器在时间轮上转多少圈后生效*/int time_slot;/*记录定时器属于时间轮上的哪个槽*/void (*cb_func)(client_data*);/*定时器回调函数*/client_data* user_data;/*用户数据*/tw_timer* next;/*指向下一个定时器*/tw_timer* prev;/*指向前一个定时器*/time_t  expire;//定时时间time_t start_time;//设定定时器的时间};class time_wheel
{public:time_wheel():cur_slot(0){for(int i=0;i<N;i++){slots[i]=NULL;/*初始化每个槽的头结点*/}}~time_wheel(){for(int i=0;i<N;i++){tw_timer* tmp = slots[i];while(tmp){slots[i]=tmp->next;delete tmp;tmp = slots[i];}}}/*根据定时器的定时值timeout创建一个定时器,并把它插入合适的槽中*/tw_timer* add_timer(int put_timeout){if(put_timeout<0){return NULL;}int ticks =0;int  timeout = put_timeout-time(NULL);if(timeout<SI)ticks = 1;elseticks = timeout/SI;/*计算待插入的定时器在时间轮上转动多少圈后触发*/int  rotation = ticks/N;/*计算定时器应该被插入哪个槽中*/int ts = (cur_slot+ticks%N)%N;/*创建定时器,它在时间轮转动rotation圈之后被触发,且位于第ts个槽中*/ tw_timer* timer = new tw_timer(rotation,ts);timer->expire = put_timeout;timer->oldrotation = rotation;if(!slots[ts])slots[ts]=timer;else{timer->next = slots[ts];slots[ts]->prev = timer;slots[ts] = timer;}return timer;}void del_timer(tw_timer* timer){if(!timer)return;int ts = timer->time_slot;if(timer == slots[ts]){slots[ts] = slots[ts]->next;if(slots[ts])slots[ts]->prev = NULL;delete timer;}else{timer ->prev->next = timer ->next;if(timer->next)timer ->next ->prev = timer ->prev;delete timer;}}void tick(){tw_timer* tmp = slots[cur_slot];while(tmp){//如果定时器要转的圈数仍大于零if(tmp->rotation>0){tmp ->rotation --;tmp= tmp->next;}else{time_t cur_time = time(NULL);if(cur_time<tmp->expire)tmp=tmp->next;else{tmp->cb_func(tmp->user_data);if(tmp ==slots[cur_slot]){slots[cur_slot] = tmp->next;delete tmp;if(slots[cur_slot]){slots[cur_slot]->prev =NULL;}tmp = slots[cur_slot];}/*如果定时器不再时间槽首端*/else{tmp->prev->next = tmp->next;if(tmp->next)tmp ->next ->prev = tmp ->prev;tw_timer* tmp2 =tmp->next;delete tmp;tmp = tmp2;//tmp 指向下一个定时器}}}}cur_slot=++cur_slot%N;}private:static const int N = 60;/*转动一周需要60时间单位*/static const int SI = 1;/*每隔1时间单位中,转动一次*/tw_timer* slots[N];/*时间轮的槽,其中每个月元素指向一个定时器链表,链表无序*/int cur_slot;/*时间轮当前槽*/};#endif


 

下面是time_wheel.main.cpp

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include"time_wheel_timer.h"using namespace std;
static time_wheel client_timer; 
void myfunc(client_data*arg)
{cout<<"timeout-->"<<"oldrotation:"<<arg->timer->oldrotation<<" now need rotation:"<<arg->timer->rotation<<" sockfd:"<<arg->sockfd<<" timer_slot:"<<arg->timer->time_slot<<" start_time:"<<arg->timer->start_time<<" expire:"<<arg->timer->expire<<" now:"<<time(NULL)<<" duration_time:"<<arg->timer->expire-arg->timer->start_time<<endl;arg =NULL;	}
void* checktick(void*arg)
{while(1){client_timer.tick();usleep(100);//后续采用信号处理}}
int main(){client_data*users = new client_data[20];srand(time(NULL));for(int i =0;i<10;i++){users[i].sockfd = i;time_t cur_time = time(NULL);tw_timer* timer = client_timer.add_timer(cur_time+rand()%100+1);if(timer){timer->user_data = &users[i];timer->cb_func = myfunc;timer->start_time = cur_time;users[i].timer = timer;}}
pthread_t pid;
pthread_create(&pid,NULL,checktick,NULL);
void* retval;
pthread_join(pid,&retval);}



 
 

编译执行

       g++ time_wheel.main.cpp -lpthread -owheel

执行结果

ky@ky-S910-X31E:~/libz/72$ ./wheel 
timeout-->oldrotation:0 now need rotation:0 sockfd:9 timer_slot:24 start_time:1404385502 expire:1404385526 now:1404385526 duration_time:24
timeout-->oldrotation:0 now need rotation:0 sockfd:8 timer_slot:25 start_time:1404385502 expire:1404385527 now:1404385527 duration_time:25
timeout-->oldrotation:0 now need rotation:0 sockfd:0 timer_slot:25 start_time:1404385502 expire:1404385527 now:1404385527 duration_time:25
timeout-->oldrotation:0 now need rotation:0 sockfd:4 timer_slot:52 start_time:1404385502 expire:1404385554 now:1404385554 duration_time:52
timeout-->oldrotation:1 now need rotation:0 sockfd:1 timer_slot:2 start_time:1404385502 expire:1404385564 now:1404385564 duration_time:62
timeout-->oldrotation:1 now need rotation:0 sockfd:7 timer_slot:15 start_time:1404385502 expire:1404385577 now:1404385577 duration_time:75
timeout-->oldrotation:1 now need rotation:0 sockfd:3 timer_slot:16 start_time:1404385502 expire:1404385578 now:1404385578 duration_time:76
timeout-->oldrotation:1 now need rotation:0 sockfd:6 timer_slot:30 start_time:1404385502 expire:1404385592 now:1404385592 duration_time:90
timeout-->oldrotation:1 now need rotation:0 sockfd:2 timer_slot:32 start_time:1404385502 expire:1404385594 now:1404385594 duration_time:92
timeout-->oldrotation:1 now need rotation:0 sockfd:5 timer_slot:35 start_time:1404385502 expire:1404385597 now:1404385597 duration_time:95

   其中oldrotation:1表示旋转圈数,need rotation:0 表示目前剩下的旋转圈数,sockfd:2表示是第几个定时器, timer_slot:32 表示定时器位于第几个槽中,start_time:1404385502 表示定时器开始定时的时间,expire:1404385597表示定时器超时时间。

 

时间轮分析

   对应时间轮分析,添加一个定时器的时间按复杂度为O(1),删除一个定时的时间复杂度也是O(1),执行一个定时器的时间复杂度为O(n),如果定时器的时间轮上的槽数越高则效率就会更好的提升

 

 

更多内容请前往个人文库

http://wenku.baidu.com/p/helpylee

 





http://chatgpt.dhexx.cn/article/Gowxm37M.shtml

相关文章

深度剖析 -- 时间轮算法(TimingWheel)是如何实现的?

前言 时间轮的应用场景很多&#xff0c;在 Netty、Akka、Quartz、ZooKeeper 、Kafka等组件中都存在时间轮的踪影。我们下面讲解的时间轮的实现以JRaft中的为例子进行讲解&#xff0c;因为JRaft这部分的代码是参考Netty的&#xff0c;所以大家也可以去Netty中去寻找源码实现。 …

Kafka原理--时间轮(延时操作)

原文网址&#xff1a;Kafka原理--时间轮(延时操作)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Kafka的时间轮的原理。 Kafka没有延迟队列功能供用户使用&#xff0c;本文介绍的延时操作仅仅是Kafka内部的使用&#xff0c;用户无法使用。 Kafka延时操作 Kafka中存在大量的…

时间轮算法HashedWheelTimer

文章目录 一.HashedWheelTimer是什么?二.能干什么?为什么需要这个东西?优点适用场景 三.怎么用?使用步骤1.引入pom2.使用举例 四.时间轮原理五.使用注意点1.一个HashedWheelTimer对象只有一个worker线程2.每次添加的任务只会执行一次3.时间轮的参数非常重要4.所有的任务都是…

时间轮-理论篇

1. 引言 定时任务再开发过程中无处不在&#xff0c;定时发送消息&#xff0c;定时更新数据库表的状态&#xff0c;Linux系统定时执行脚本等等。这些操作都离不开定时任务&#xff0c;那么这些定时任务是怎么实现的是否又去想过。如果让开发者自己去实现一个定时任务又该怎么实…

定时任务的实现原理:时间轮算法

文章目录 前言时间轮定时使用方式时间轮定时内部原理时间轮定时源码剖析构造方法添加任务工作线程启动工作线程run方法指针跳动将队列任务放入时间轮中链表任务遍历定时任务执行 前言 最近在思考实现定时任务的几种方式&#xff0c;比如 quartz&#xff0c;delay queue&#xf…

时间轮-Java实现篇

在前面的文章《时间轮-理论篇》讲了时间轮的一些理论知识&#xff0c;然后根据理论知识。我们自己来实现一个简单的时间轮。 1. 理论抽象 将时间轮的理论进行抽象&#xff0c;主要有两个方面&#xff1a; 时间轮的转动每一个时间间隔任务的处理&#xff0c;从时间间隔的Buke…

时间轮timewheel算法

时间轮是个不太常见&#xff0c;但在部分场景有较高使用价值的工具。 时间轮常用于延时任务&#xff0c;在Netty、akka、Quartz、Zookeeper等高性能组件中都存在时间轮定时器的踪影。 从定时任务说起 自然界中定时任务无处不在&#xff0c;太阳每天东升西落&#xff0c;候鸟的…

定时器实现原理——时间轮

时间轮 时间轮算法是通过一个时间轮去维护定时任务&#xff0c;按照一定的时间单位对时间轮进行划分刻度。然后根据任务延时计算任务落在该时间轮的第几个刻度上&#xff0c;如果任务时长超出了刻度数量&#xff0c;则需要增加一个参数记录时间轮需要转动的圈数。 简单时间轮…

时间轮算法

一、时间轮算法 1. 时间轮的基本概念 Kafka中存在大量的延时操作&#xff0c;如延时生产、延时消费等&#xff1b;而JDK中自带的 Timer 和 DelayQueue 的插入和删除操作的平均复杂度为 O&#xff08;nlogn&#xff09;&#xff0c;无法满足 Kafka 的高性能要求&#xff0c;因…

时间轮算法(TimingWheel)

时间轮算法的应用非常广泛&#xff0c;在 Dubbo、Netty、Kafka、ZooKeeper、Quartz 的组件中都有时间轮思想的应用&#xff0c;甚至在 Linux 内核中都有用到。 其思想理论基础可以参考论文&#xff1a;http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pd…

那些惊艳的算法们(三)—— 时间轮

从定时任务说起 自然界中定时任务无处不在&#xff0c;太阳每天东升西落&#xff0c;候鸟的迁徙&#xff0c;树木的年轮&#xff0c;人们每天按时上班&#xff0c;每个月按时发工资、交房租&#xff0c;四季轮换&#xff0c;潮涨潮落&#xff0c;等等&#xff0c;从某种意义上…

1、时间轮

一、什么是时间轮&#xff1f; 作为一个粗人&#xff0c;咱不扯什么高级的词汇&#xff0c;直接上图&#xff1a; 上面是一张时间轮的示意图&#xff0c;可以看到&#xff0c;这个时间轮就像一个钟表一样&#xff0c;它有刻度&#xff0c;图中画了9个格子&#xff0c;每个格子…

浅谈时间轮算法

浅谈时间轮算法 基于队列的定时任务执行模型缺陷 在计算机世界中&#xff0c;只有待解决的问题变得大规模后&#xff0c;算法的价值才能够最大化的体现。时间轮算法可以将插入和删除操作的时间复杂度都降为 O(1)&#xff0c;在大规模问题下还能够达到非常好的运行效果。 如果…

时间轮(TimingWheel)算法总结

通过阅读篇文章您可以很容易理解平时所使用的开源框架是如何进行任务调度的。而且对于以后业务上碰到需要做时间任务调度的需求&#xff0c;也可以尝试着用实践论算法去实现。 一、时间轮的应用 其实早在1987年&#xff0c;时间轮算法的论文已经发布。论文名称&#xff1a;Hash…

时间轮 (史上最全)

缓存之王 Caffeine 中&#xff0c;涉及到100w级、1000W级、甚至亿级元素的过期问题&#xff0c;如何进行高性能的定时调度&#xff0c;是一个难题。 注&#xff1a; 本文从 对 海量调度任务场景中&#xff0c; 高性能的时间轮算法&#xff0c; 做了一个 系统化、由浅入深的 穿透…

时间轮(Timing Wheel)案例和原理

什么是时间轮(Timing Wheel) 时间轮(Timing Wheel)是George Varghese和Tony Lauck在1996年的论文Hashed and Hierarchical Timing Wheels: data structures to efficiently implement a timer facility实现的&#xff0c;它在Linux内核中使用广泛&#xff0c;是Linux内核定时器…

时间轮(TimingWheel)

一、什么是时间轮 时间轮其实就是一种环形的数据结构&#xff0c;可以想象成时钟&#xff0c;分成很多格子&#xff0c;一个格子代表一段时间&#xff08;这个时间越短&#xff0c;Timer的精度越高&#xff09;。并用一个双向链表存储放在该格子上的延时任务&#xff0c;同时一…

高性能定时器3——时间轮

​ 在网络程序中我们通常要处理三种事件&#xff0c;网络I/O事件、信号以及定时事件&#xff0c;我们可以使用I/O复用系统调用&#xff08;select、poll、epoll&#xff09;将这三类事件进行统一处理。我们通常使用定时器来检测一个客户端的活动状态&#xff0c;服务器程序通常…

C#,斐波那契数列(Fibonacci Sequence)的八种算法与源代码

一、莱昂纳多斐波那契&#xff08;Leonardo Fibonacci&#xff09; 斐波那契公元1170年生于意大利比萨&#xff0c;卒于1250年&#xff0c;被人称作“比萨的莱昂纳多”&#xff0c;是一名闻名于欧洲的数学家&#xff0c;其主要的著作有《算盘书》、《实用几何》和《四艺经》等。…

matlab斐波那契数列画图,斐波拉契数列 斐波那契数列 matlab程序

斐波那契数列数列从第3项开始,每一项都等于前两项之和。 例子:数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368 应用: 生活斐波那契 斐波那契数列中的斐波脾气有点大,表面温和到别人误以为很好欺…