基于时间轮的定时器

这里关于时间轮的介绍,我觉得这篇博客介绍的很清晰:时间轮算法

 

                                                1b80c724801c46f0b5e89c14adf2fadf-image.png

总结:

               就是实现一个时钟,首先时钟有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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值