所谓时间轮就是一个循环数组,每过一个时钟周期,数组下标前进一格。每个数组的entry后面挂着一个list, 有点类似于哈希链表。哈希表链表node是因为散列冲突, 时间轮链表node是因为对数组size取模有相同的余数。比如有6个node: 1, 4,7, 10, 13, 16 。数组size = 6, 那么时间轮里的数据如下图所示
因为:
1,7,13对6做%运算结果都相同
4,10, 16对6做%运算结果都相同
那么怎么用时间轮去实现一个timer呢,算法如下:
list node 存储一个关键信息rotation(代表时间轮要走几轮), rotation = timer % 数组size. 以上为例,有 1, 4 , 7 , 10, 13 , 16 这6个timer, 那么:
timer 1 , rolation = 1 /6 = 0
timer 4, rolation = 4 / 6 = 0
timer 7, rolation = 7 / 6 = 1
...
timer 16, rolation = 16 / 6 = 2.
当时间轮走到某个数组元素时,遍历该数组元素之后的list. 对于每个list node , 如果其rotation = 0, 则说明时间
到了, 执行其callback 然后删除该node. 否则rotation--.
具体代码:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define TIME_WHEEL_SIZE 6
typedef void (*func)(int data);
struct timer_node
{
struct timer_node *next;
int rotation;
func proc; //timeout callback
int data;
};
struct timer_wheel
{
struct timer_node *slot[TIME_WHEEL_SIZE];
int current;
};
struct timer_wheel timer = {{0}, 0};
void tick(int signo)
{
struct timer_node **cur = &timer.slot[timer.current];
while (*cur)
{
struct timer_node *curr = *cur;
if (curr->rotation > 0) //time do not out, wait until the next round
{
curr->rotation--;
cur = &curr->next;
}
else //timeout, execute callback, and delete timer node
{
curr->proc(curr->data);
*cur = curr->next;
free(curr);
}
}
timer.current = (timer.current + 1) % TIME_WHEEL_SIZE;
alarm(1); // trigger the alarm signal in 1s
}
void add_timer(int len, func action)
{
int pos = (len + timer.current) % TIME_WHEEL_SIZE;
struct timer_node *node = (timer_node *)malloc(sizeof(struct timer_node));
node->next = timer.slot[pos];
timer.slot[pos] = node;
node->rotation = len / TIME_WHEEL_SIZE;
node->data = 0;
node->proc = action;
}
int g_sec1 = 0;
void do_time1(int data) //timer callback
{
printf("%s timer %d\n", __FUNCTION__, g_sec1++);
add_timer(1, do_time1); //add the timer again
}
void do_time9(int data)
{
printf("%s timer %d\n", __FUNCTION__, g_sec1);
add_timer(9, do_time9);
}
int main()
{
signal(SIGALRM, tick);
alarm(1);
add_timer(1, do_time1);
add_timer(9, do_time9);
while(1) pause();
return 0;
}
result: