定时器之多层级时间轮

定时器之多层级时间轮

定时器应用

  • 心跳检测
  • 倒计时
  • 武器/技能冷却
  • 其它需要使用超时机制的功能

数据结构

实现定时器常见的数据结构有红黑树,最小堆,跳表,时间轮。这里需要特别说明的是对于最小堆,我们需要借助map来快速索引到节点。

实现代码

此处用时间轮来实现定时器

spinlock.h

#ifndef SPINLOCK_H
#define SPINLOCK_H

struct spinlock {
	int lock;
};

void spinlock_init(struct spinlock *lock) {
	lock->lock = 0;
}

void spinlock_lock(struct spinlock *lock) {
	//原子操作,将1写入&lock->lock,同时返回 &lock->lock 之前的值
	while (__sync_lock_test_and_set(&lock->lock, 1)) {}
}

int spinlock_trylock(struct spinlock *lock) {
	return __sync_lock_test_and_set(&lock->lock, 1) == 0;
}

void spinlock_unlock(struct spinlock *lock) {
	__sync_lock_release(&lock->lock);
}

void spinlock_destroy(struct spinlock *lock) {
	(void) lock;
}

#endif

timewheel.h

#ifndef _MARK_TIMEWHEEL_
#define _MARK_TIMEWHEEL_

#include <stdint.h>
#include <assert.h>
#include <stdio.h>

#define TIME_NEAR_SHIFT 8
#define TIME_NEAR (1 << TIME_NEAR_SHIFT)
#define TIME_LEVEL_SHIFT 6
#define TIME_LEVEL (1 << TIME_LEVEL_SHIFT)
#define TIME_NEAR_MASK (TIME_NEAR-1)
#define TIME_LEVEL_MASK (TIME_LEVEL-1)

typedef struct timer_node timer_node_t;
typedef void (*handler_pt) (struct timer_node *node);

struct timer_node {
	struct timer_node *next;
	uint32_t expire;//下一次过期时间
    handler_pt callback;
    uint8_t cancel;//定时器是否删除,1删除不执行
	uint8_t repeat; //是否重复执行 1 重复执行
	uint32_t timing;//定时时间
	int id; // 代表定时器节点id
};

// 添加定时器
timer_node_t* add_timer(int time, handler_pt func, int threadid,uint8_t repeat);

//时间轮定时任务检测
void expire_timer(void);

// 删除定时器
void del_timer(timer_node_t* node);

// 初始化定时器
void init_timer(void);

// 清除定时器
void clear_timer();

#endif

timewheel.c

#include "spinlock.h"
#include "timewheel.h"
#include <string.h>
#include <stddef.h>
#include <stdlib.h>

#if defined(__APPLE__)
#include <AvailabilityMacros.h>
#include <sys/time.h>
#include <mach/task.h>
#include <mach/mach.h>
#else
#include <time.h>
#endif

//每次映射是将下层单元格数据映射到上一层

//数组每个元素的,和单时间轮一样
typedef struct link_list {
	timer_node_t head;
	timer_node_t *tail;
}link_list_t;

//多层时间轮  5个层级
typedef struct timer {
	link_list_t near[TIME_NEAR];//第一个层级  2^8 = 256
	link_list_t t[4][TIME_LEVEL];//2-5 层   2^6 = 64
	struct spinlock lock;
	uint32_t time;
	uint64_t current;
	uint64_t current_point;//当前时间点,基本单位10ms
}s_timer_t;

static s_timer_t * TI = NULL;

timer_node_t *
link_clear(link_list_t *list) {
	timer_node_t * ret = list->head.next;
	list->head.next = 0;
	list->tail = &(list->head);

	return ret;
}

//节点映射,尾插法
void
link(link_list_t *list, timer_node_t *node) {
	list->tail->next = node;
	list->tail = node;
	node->next=0;
}

//将时间事件节点插入时间轮
void
add_node(s_timer_t *T, timer_node_t *node) {
	uint32_t time=node->expire;//过期时间   uint32_t 正好int类型 4个字节32位
	uint32_t current_time=T->time;//当前时间点
	uint32_t msec = time - current_time;//还有多久要被执行任务的时间
	if (msec < TIME_NEAR) { //[0, 0x100) 第一层级
		link(&T->near[time&TIME_NEAR_MASK],node); //time%256 在[0,255] 数组映射
	} else if (msec < (1 << (TIME_NEAR_SHIFT+TIME_LEVEL_SHIFT))) {//[0x100, 0x4000)  第二层级
		link(&T->t[0][((time>>TIME_NEAR_SHIFT) & TIME_LEVEL_MASK)],node);	
	} else if (msec < (1 << (TIME_NEAR_SHIFT+2*TIME_LEVEL_SHIFT))) {//[0x4000, 0x100000)
		link(&T->t[1][((time>>(TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)],node);	
	} else if (msec < (1 << (TIME_NEAR_SHIFT+3*TIME_LEVEL_SHIFT))) {//[0x100000, 0x4000000)
		link(&T->t[2][((time>>(TIME_NEAR_SHIFT + 2*TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)],node);	
	} else {//[0x4000000, 0xffffffff]
		link(&T->t[3][((time>>(TIME_NEAR_SHIFT + 3*TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)],node);	
	}
}

//定时器添加
timer_node_t*
add_timer(int time, handler_pt func, int threadid,uint8_t repeat) {
	timer_node_t *node = (timer_node_t *)malloc(sizeof(*node));
	spinlock_lock(&TI->lock);
	node->timing = time;
	node->expire = time+TI->time;// 每10ms加1 0
	node->callback = func;
	node->id = threadid;
	node->repeat = repeat;
	if (time <= 0) {
		node->callback(node);
		free(node);
		spinlock_unlock(&TI->lock);
		return NULL;
	}
	add_node(TI, node);
	spinlock_unlock(&TI->lock);
	printf("add time event success\n");
	return node;
}

//这⾥发⽣重新映射,将i+1层级idx格⼦中的定时任务重新映射到i层级中
void
move_list(s_timer_t *T, int level, int idx) {
	timer_node_t *current = link_clear(&T->t[level][idx]);
	while (current) {
		timer_node_t *temp=current->next;
		add_node(T,current);
		current=temp;
	}
}

//由下往上重新映射 
void
timer_shift(s_timer_t *T) {
	int mask = TIME_NEAR;//256
	uint32_t ct = ++T->time; // ++ ⼀次代表10ms,可以认为时间相对于服务运行时间
	if (ct == 0) {//溢出
		move_list(T, 3, 0);
	} else {
		// ct / 256
		uint32_t time = ct >> TIME_NEAR_SHIFT;
		int i=0;
		// ct % 256 == 0  说明是否移动到了 不同层级的 最后⼀格
		while ((ct & (mask-1))==0) {
			int idx=time & TIME_LEVEL_MASK;
			if (idx!=0) {
				move_list(T, i, idx);//这⾥发⽣重新映射,将i+1层级idx格⼦中的定时任务重新映射到i层级中
				break;				
			}
			mask <<= TIME_LEVEL_SHIFT;
			time >>= TIME_LEVEL_SHIFT;
			++i;
		}
	}
}

//执行层级1 数组中每个元素对应的链表定时器
void
dispatch_list(timer_node_t *current) {
	do {
		timer_node_t * temp = current;
		current=current->next;
        if (temp->cancel == 0)
		{
			
			temp->callback(temp);
			if(temp->repeat == 1){
				//重新注册就可以了
				add_timer(temp->timing,temp->callback,temp->id,temp->repeat);
			}
		}
		free(temp);
	} while (current);
}

//执行定时器
void
timer_execute(s_timer_t *T) {
	//执行层级1 的定时器
	int idx = T->time & TIME_NEAR_MASK;// T->time % 255
	//遍历层级1的定时器
	while (T->near[idx].head.next) {
		
		timer_node_t *current = link_clear(&T->near[idx]);
		spinlock_unlock(&T->lock);
		dispatch_list(current);
		spinlock_lock(&T->lock);
	}
}

//执行定时器并重新映射
void 
timer_update(s_timer_t *T) {
	
	spinlock_lock(&T->lock);
	timer_execute(T);
	timer_shift(T);
	timer_execute(T);
	spinlock_unlock(&T->lock);
}

void
del_timer(timer_node_t *node) {
    node->cancel = 1;
}

//创建时间轮
s_timer_t *
timer_create_timer() {
	s_timer_t *r=(s_timer_t *)malloc(sizeof(s_timer_t));
	memset(r,0,sizeof(*r));
	int i,j;
	//第一层数组
	for (i=0;i<TIME_NEAR;i++) {
		link_clear(&r->near[i]);
	}
	//第2-5层数组
	for (i=0;i<4;i++) {
		for (j=0;j<TIME_LEVEL;j++) {
			link_clear(&r->t[i][j]);
		}
	}
	spinlock_init(&r->lock);
	r->current = 0;
	return r;
}

//时间间隔10ms
uint64_t
gettime() {
	uint64_t t;
#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
	struct timespec ti;
	clock_gettime(CLOCK_MONOTONIC, &ti);
	t = (uint64_t)ti.tv_sec * 100;
	t += ti.tv_nsec / 10000000;
#else
	struct timeval tv;
	gettimeofday(&tv, NULL);
	t = (uint64_t)tv.tv_sec * 100;
	t += tv.tv_usec / 10000;
#endif
	return t;
}

void
expire_timer(void) {
	uint64_t cp = gettime();
	
	if (cp != TI->current_point) {
		uint32_t diff = (uint32_t)(cp - TI->current_point);//diff 单位10ms
		TI->current_point = cp;
		int i;
		//10ms 轮询一次
		for (i=0; i<diff; i++) {
			timer_update(TI);
		}
	}
}

void 
init_timer(void) {
	TI = timer_create_timer();
	TI->current_point = gettime();
}

void
clear_timer() {
	int i,j;
	for (i=0;i<TIME_NEAR;i++) {
		link_list_t * list = &TI->near[i];
		timer_node_t* current = list->head.next;
		while(current) {
			timer_node_t * temp = current;
			current = current->next;
			free(temp);
		}
		link_clear(&TI->near[i]);
	}
	for (i=0;i<4;i++) {
		for (j=0;j<TIME_LEVEL;j++) {
			link_list_t * list = &TI->t[i][j];
			timer_node_t* current = list->head.next;
			while (current) {
				timer_node_t * temp = current;
				current = current->next;
				free(temp);
			}
			link_clear(&TI->t[i][j]);
		}
	}
}

测试代码 main.c

#include <stdio.h>
#include <unistd.h>

#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include "timewheel.h"
//时间轮 多线程环境下锁的操作粒度小

struct context {
	int quit;//1 退出
    int thread;//线程数
};

struct thread_param {
	struct context *ctx;
	int id;//标识线程id
};

static struct context ctx = {0};

void do_timer(timer_node_t *node) {
    printf("timer expired:%d - thread-id:%d\n", node->expire, node->id);
}

//线程工作
void* thread_worker(void *p) {
	struct thread_param *tp = p;
	int id = tp->id;
    struct context *ctx = tp->ctx;
    
    //线程没有停止,一直运行
	while (!ctx->quit) {
        
        int expire = rand() % 200;// [0,199]
        add_timer(expire, do_timer,id,0);
        usleep(expire*(10-1)*1000); 
    }
    printf("thread_worker:%d exit!\n", id);
    return NULL;
}

void do_quit(timer_node_t * node) {
    ctx.quit = 1;
}

void do_test(timer_node_t *node){
    printf("执行定时器%d,每%d执行一次\n",node->id,node->timing*10/1000);
}

int main() {
    #if 1
    srand(time(NULL));
    ctx.thread = 8;
    pthread_t pid[ctx.thread];

    init_timer();
    add_timer(6000, do_quit, 100,0);//60s 后退出 6000*10/ 1000 = 60s  
    struct thread_param task_thread_p[ctx.thread];
    int i;
    for (i = 0; i < ctx.thread; i++) {
        task_thread_p[i].id = i;
        task_thread_p[i].ctx = &ctx;
        if (pthread_create(&pid[i], NULL, thread_worker, &task_thread_p[i])) {
            fprintf(stderr, "create thread failed\n");
            exit(1);
        }
    }

    while (!ctx.quit) {
        expire_timer();
        usleep(2500);
    }
    clear_timer();
    for (i = 0; i < ctx.thread; i++) {
		pthread_join(pid[i], NULL);
    }
    printf("all thread is closed\n");
    #else
        srand(time(NULL));
        init_timer();

        add_timer(500, do_test, 100,1);
        add_timer(600, do_test, 101,1);
        while (!0) {
            expire_timer();
            usleep(2500);
        }
        clear_timer();
    #endif

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱写代码的马良

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值