这里关于时间轮的介绍,我觉得这篇博客介绍的很清晰:时间轮算法
总结:
就是实现一个时钟,首先时钟有N个槽,并且以si时间滴答一次,那么对于一个相对时间t来说,它需要滴答t/si次,才能执行,那么时钟需要到达(cur+t/si)%N,才能执行这个定时器,但是有的定时器即使在一个槽子,但是可能是今天的t时刻,也可能是明天的t时刻,也就是说转的圈可能不同,所以这里在用一个变量rotation(圈数)来记录每个定时器需要转的圈数,然后每次滴答到达这个槽子时,就需要把这个槽子里的链表的所有节点的rotation减1,如果有0的,就直接执行这个定时器。
下面是code:
#ifndef TIME_WHEEL_TIMER
#define TIME_WHEEL_TIMER
#include <time.h>
#include <netinet/in.h>
#include <stdio.h>
#define BUFFER_SIZE 64
class tw_timer;
/* 绑定 socket 和定时器 */
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):
next(NULL),prev(NULL),rotation(rot),time_slot(ts){};
public:
int rotation; /* 记录定时器在时间轮转多少圈后生效*/
int time_slot; /* 记录定时器属于时间轮上哪个槽 */
void (*cb_func)(client_data*); /*定时器回调函数 */
client_data *user_data; /* 客户数据 */
tw_timer* next;
tw_timer* prev;
};
//时间轮
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 timeout)
{
if(timeout<0)
return NULL;
int ticks=0;
//如果插入定时器的超时值小于时钟滴答一次的时间,就向上取1
if(timeout<SI)
ticks=1;
else
ticks=timeout/SI;
/* 计算待插入的定时器在时间轮转动多少圈后被触发 */
int rotation=ticks/N;
/* 计算待插入的定时器应该被插入哪个槽中 */
int ts=(cur_slot+(ticks%N))%N;
tw_timer* timer=new tw_timer(rotation,ts);
//第 ts 个尚无定时器
if(!slots[ts])
{
printf("add timer,rotation is %d,ts is %d,cur_slot is %d\n",
rotation,ts,cur_slot );
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;
}
}
//SI时间到后,调用该函数,时间轮向前移动一个槽
void tick()
{
tw_timer* tmp=slots[cur_slot];
printf("current slot is %d\n",cur_slot );
while(tmp)
{
printf("tick the timer once\n");
if(tmp->rotation>0)
{
tmp->rotation--;
tmp=tmp->next;
}
else{
tmp->cb_func(tmp->user_data);
if(tmp==slots[cur_slot])
{
printf("delete header in cur_slot\n");
slots[cur_slot]=tmp->next;
delete tmp;
if(slots[cur_slot])
slots[cur_slot]->prev=NULL;
tmp=slots[cur_slot];
}
else{
timer->prev->next=timer->next;
if(timer->next)
{
timer->next->prev=timer->prev;
}
tw_timer* tmp2=tmp->next;
delete tmp;
tmp=tmp2;
}
}
}
cur_slot=++cur_slot % N; //更新时间轮的当前槽
}
private:
/* 时间轮上槽的数目 */
static const int N=60;
/* 每隔 1s 转动一次 */
static const int SI=1;
tw_timer* slots[N];
int cur_slot;
};
#endif