从头开始读skynet源码(5)skynet.start服务器启动之后做了什么之thread_timer(skynet定时器)

本文深入分析skynet定时器结构和初始化过程,解释其为何采用时间轮设计,并详细阐述定时器线程每2.5毫秒如何刷新及网络线程如何同步时间。
摘要由CSDN通过智能技术生成

继续按照skynet.start中的顺序分析。分析skynet定时器。启动各个线程都会做类似的初始化。

定时器分析

这里要做一个前置理解,定时器,是只需要关心最近即将到来的任务,而不用关心距离现在比较远的任务,例如
1.你注册了100个定时任务,定时任务按照时间排序后
2.A任务2s后触发,B任务3h后触发,C任务h后触发…
3.当刷新时间的时候,需要去拿到即将到来的任务而需要关心之后的任务,这里就是只需要关心A任务,也就是你不需要去遍历所有任务列表拿到即将发生的任务,因为B之后的任务包括B,时间间隔太远了都不需要去关心。

所以定时器的设计需要:
1.查询快
2.做好排序
3.删除做完的任务之后不影响之前结构

常见的设计例如使用最小堆,时间轮,红黑树,而skynet使用的是时间轮。

时间轮的设计就类似于我们生活中的钟表,大概说就是,秒针走一圈,分针走一格,分针走一圈,时针走一格。
运用到程序中,大概就是,
1.我们只关心秒针的任务。
2.秒针的一圈任务执行完了之后,拿分针一格的任务分配到秒针各秒中。这样就我们每次都只会从秒针的任务中拿到需要执行的任务,和第1点一致。
3.分针的一圈任务执行完了之后,拿时针一格的任务分配到分针各分中。

先看定时器结构

#define TIME_NEAR_SHIFT 8					// 临近时间转移量
#define TIME_NEAR (1 << TIME_NEAR_SHIFT)	// 临近时间量 0x10000
#define TIME_LEVEL_SHIFT 6					// 别的时间等级转移量
#define TIME_LEVEL (1 << TIME_LEVEL_SHIFT)	// 时间等级 0x1100
#define TIME_NEAR_MASK (TIME_NEAR-1)		// 临近时间掩码 0x1111
#define TIME_LEVEL_MASK (TIME_LEVEL-1)		// 别的时间等级掩码 0x1011

struct timer_event {
   
    uint32_t handle; //即是设置定时器的来源,又是超时消息发送的目标
    int session; //session,一个增ID,溢出了从1开始,所以不要设时间很长的timer
};

// 时间节点链表
struct timer_node {
   
	struct timer_node *next;	// 下一个节点地址
	uint32_t expire;			// 到期时间
};

// 定时器任务链表,
// 链表每个节点是一个时间节点链表
// 这里一定要清楚,定时器任务链表上每个节点是一个时间节点链表
// 即同一时间的任务链表
struct link_list {
   
	struct timer_node head;		// 头节点
	struct timer_node *tail;	// 尾节点
};

struct timer {
   
	// (临近时间)8 + 4*(另外等级)6 = 32,刚好32位
	struct link_list near[TIME_NEAR];	// 临近时间的定时器任务链表,也就是我上面说的秒针
	struct link_list t[4][TIME_LEVEL];	// 剩下的四个等级的任务链表

	struct spinlock lock;				// 自旋锁
	uint32_t time;						// 服务器经过的的tick数,每10毫秒tick一次
	uint32_t starttime;					// 程序启动时间戳
	uint64_t current;					// 启动到现在的耗时,精度10毫秒级
	uint64_t current_point;				// 当前时间,精度10毫秒级
};

定时器初始化

skynet_start中调用 skynet_timer_init初始化,在我的《从头开始读skynet源码(1)》中有写。

// skynet_start中调用 skynet_timer_init初始化
void 
skynet_timer_init(void) {
   
	TI = timer_create_timer();
	uint32_t current = 0;
	systime(&TI->starttime, &current);
	TI->current = current;
	TI->current_point = gettime();
}


static struct timer *
timer_create_timer() {
   
	struct timer *r=(struct timer *)skynet_malloc(sizeof(struct timer));
	memset(r,0,sizeof(*r));

	int i,j;

	// 重置临近时间定时器任务链表
	for (i=0;i<TIME_NEAR;i++) {
   
		link_clear(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值