0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

定时器作用及实现定时器数据结构选取介绍2

jf_78858299 来源:程序员不是码农 作者:程序员不是码农 2023-04-21 15:20 次阅读

Skynet定时器实现方案

skynet中定时器数据结构

采用时间轮方式,hash表+链表实现,

struct timer_node {  //时间节点
 struct timer_node *next;
    uint32_t expire; //到期滴答数
};
struct link_list {  // 链表
  struct timer_node head;
  struct timer_node *tail;
};
struct timer {
 struct link_list near[256];  // 即将到来的定时器
    struct link_list t[4][64]; // 相对较遥远的定时器
    struct spinlock lock;
    uint32_t time;  // 记录当前滴答数
    uint64_t starttime;
    uint64_t current;
    uint64_t current_point;
};

其中time32位无符号整数, 记录时间片对应数组near[256] ,表示即将到来的定时任务, t[4][64],表示较为遥远的定时任务。

定时器执行流程

图片

skynet_time_wheel

t[3] t[2] t[1] t[0] near
26-32位 20-26位 14-20位 8-14位 0-8位
[2^(8+6x3),2^(8+6x4)-1] [2^(8+6x2),2^(8+6x3)-1] [2^(8+6),2^(8+6x2)-1] [2^8,2^(8+6) -1] [0,2^8-1]
  • 首先检查节点的expiretime的高24位是否相等,相等则将该节点添加到expire低8位值对应数组near的元素的链表中,不相等则进行下一步
  • 检查expiretime的高18位是否相等,相等则将该节点添加到expire低第9位到第14位对应的6位二进制值对应数组t[0]的元素的链表中,否则进行下一步
  • 检查expiretime的高12位是否相等,相等则将该节点添加到expire低第15位到第20位对应的6位二进制值对应数组t[1]的元素的链表中,如果不相等则进行下一步
  • 检查expiretime的高6位是否相等,相等则将该节点添加到expire低第21位到第26位对应的6位二进制值对应数组t[2]的元素的链表中,如果不相等则进行下一步
  • 将该节点添加到expire低第27位到第32位对应的6位二进制值对应数组t[3]的元素的链表中

以下为具体实现,仅贴出主要接口,具体细节请参考skynet源代码。

定时器初始化
// skynet_start.c
// skynet 启动入口
void
skynet_start(struct skynet_config * config) {
    ...
    skynet_timer_init();
    ...
}
// skynet_timer.c
void
skynet_timer_init(void) {
    // 创建全局timer结构 TI
    TI  = timer_create_timer();
    uint32_t current = 0;
    systime(&TI->starttime, ¤t);
    TI->current = current;
    TI->current_point = gettime();
}
添加定时器

通过skynet_server.c中的cmd_timeout调用skynet_timeout添加新的定时器

// TI为全局的定时器指针
static struct timer * TI = NULL; 
int skynet_timeout(uint32_t handle, int time, int session) {
    ...
    struct timer_event event;
    event.handle = handle;  // callback
    eveng.session = session;
    // 添加新的定时器节点
    timer_add(TI, &event, sizeof(event), time);
    return session;
}
// 添加新的定时器节点
static void timer_add(struct timer *T, void 8arg, size_t sz, int time) {
    // 给timer_node指针分配空间,还需要分配timer_node + timer_event大小的空间,
    // 之后通过node + 1可获得timer_event数据
    struct timer_node *node = (struct timer_node *)skynet_malloc(sizeof(*node)+sz);
    memcpy(node+1,arg,sz);
    SPIN_LOCK(T);
    node->expire=time+T->time;
    add_node(T, node);
    SPIN_UNLOCK(T);
}

// 添加到定时器链表里,如果定时器的到期滴答数跟当前比较近(<2^8),表示即将触发定时器添加到T->near数组里
// 否则根据差值大小添加到对应的T->T[i]中
static void add_node(struct timer *T, struct timer_node *node) {
    ...
}
驱动方式

skynet启动时,会创建一个线程专门跑定时器,每帧(0.0025s)调用skynet_updatetime()

// skynet_start.c
static void * 
thread_timer(void *p) {
    struct monitor * m = p;
    skynet_initthread(THREAD_TIMER);
    for (;;) {
        skynet_updatetime();  // 调用timer_update
        skynet_socket_updatetime();
        CHECK_ABORT
        wakeup(m,m->count-1);
        usleep(2500);  // 2500微秒 = 0.0025s
        if (SIG) {
            signal_hup();
            SIG = 0;
        }
    }
    ...
}

每个定时器设置一个到期滴答数,与当前系统的滴答数(启动时为0,1滴答1滴答往后跳,1滴答==0.01s ) 比较得到差值interval;

如果interval比较小(0 <= interval <= 2^8-1),表示定时器即将到来,保存在第一部分前2^8个定时器链表中;否则找到属于第二部分对用的层级中。

// skynet_timer.c
void 
skynet_updatetime(void) {
    ...
    uint32_t diff = (uint32_t)(cp - TI->current_point); 
    TI->current_point = cp;
    TI->current += diff;
    // diff单位为0.01s
    for (i = 0; i < diff; i++){
        timer_update(TI);        
    }
}
static void
timer_update(struct timer *T) {
    SPIN_LOCK(T);
    timer_execute(T); // 检查T->near是否位空,有就处理到期定时器
    timer_shift(T);  // 时间片time++,移动高24位的链表
    timer_execute(T);
    SPIN_UNLOCK(T);
}
// 每帧从T->near中触发到期得定时器
static inline void
timer_execute(struct timer *T) {
  ...
}
// 遍历处理定时器链表中所有的定时器
static inline void
dispatch_list(struct timer_node *current) {
    ...
}
// 将高24位对应的4个6位的数组中的各个元素的链表往低位移
static void
timer_shift(struct timer *T) {
    ...
}
// 将level层的idx位置的定时器链表从当前位置删除,并重新add_node
static void move_list(struct timer *T, int level, int idx) {

}

最小堆实现定时器

最小堆实现例子:boost.asio采用二叉树,go采用四叉树, libuv

具体实现略。

总结

本文主要介绍定时器作用,实现定时器数据结构选取,并详细介绍了跳表,红黑树,时间轮实现定时器的思路和方法。

Python人工智能编程分享 Python 相关的技术文章、工具资料视频教程等。专注Python编程开发学习以及人工智能、机器学习、自然语言处理、深度学习、图像识别、语音识别无人驾驶等前沿AI技术学习!

公众号

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    10992

    浏览量

    206744
  • 定时器
    +关注

    关注

    23

    文章

    3148

    浏览量

    112051
  • 数据结构
    +关注

    关注

    3

    文章

    564

    浏览量

    39905
收藏 人收藏

    评论

    相关推荐

    介绍STM32定时器的使用

    一、定时器介绍STM32F1 的定时器功能十分强大,有 TIM1 和 TIM8 等高级定时器,也有 TIM2~TIM5 等通用
    发表于 08-13 08:05

    STM32 Cubemx 定时器介绍以及应用 精选资料分享

    本篇文章介绍定时器的使用方法,并应用流水灯,以及秒表应用展示:链接定时器作用定时器(Timer)最基本的功能就是
    发表于 08-19 06:01

    软件定时器的特点和原理

    本文介绍了软件定时器的特点和原理,并从时钟节拍,数据结构定时器操作等角度分析,实现了基于STM32的软件
    发表于 08-19 08:29

    stm32定时器介绍

    定时器介绍此处引用普中总结的知识, STM32中一共有11个定时器,其中2个高级控制定时器,4个通用定时
    发表于 08-19 08:16

    定时器作用

    :pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。二、定时器几个概念1.硬件定时器一般指定时器时基是由硬件中断产生的,
    发表于 12-14 07:52

    什么是软件定时器?基于STM32的软件定时器该怎样去实现

    目录1.什么是软件定时器2.软件定时器实现原理3.基于STM32的软件定时器3.1 时钟节拍3.2
    发表于 12-22 07:47

    EPIT定时器与GPT定时器简单介绍

    EPIT定时器与GPT定时器简单介绍一、EPIT定时器1、EPIT定时器简介2、EPIT
    发表于 01-06 07:46

    STC89C52定时器介绍

    的连接和运转均在单片机内部完成。定时器作用:1.用于计数系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作2.替代长时间的Delay
    发表于 01-13 08:05

    定时器是什么?使用定时器关注的问题

    时钟频率计算举例2定时器基本配置结构体3、定时器相关库函数四、定时翻转LED灯状态(定时中断方
    发表于 01-21 07:00

    详细介绍定时器定时器中断

    在测量控制系统中,常常需要实时时钟,以实现定时控制、定时测量或定时中断等。也常需要计数器以实现对外部事件的计数。MCS-51单片机中有两个(
    的头像 发表于 02-09 14:00 1.7w次阅读
    详细<b class='flag-5'>介绍</b><b class='flag-5'>定时器</b>和<b class='flag-5'>定时器</b>中断

    STM32定时器-基本定时器

    目录定时器分类基本定时器功能框图讲解基本定时器功能时钟源计数器时钟计数器自动重装载寄存器定时时间的计算定时器初始化
    发表于 11-23 18:21 28次下载
    STM32<b class='flag-5'>定时器</b>-基本<b class='flag-5'>定时器</b>

    STM32——高级定时器、通用定时器、基本定时器的区别

    STM32——高级定时器、通用定时器、基本定时器的区别
    发表于 11-26 15:21 109次下载
    STM32——高级<b class='flag-5'>定时器</b>、通用<b class='flag-5'>定时器</b>、基本<b class='flag-5'>定时器</b>的区别

    定时器作用实现定时器数据结构选取介绍1

    定时器在各种场景都需要用到,比如游戏的Buff实现,Redis中的过期任务,Linux中的定时任务等等。顾名思义,定时器的主要用途是执行定时
    的头像 发表于 04-21 15:20 873次阅读
    <b class='flag-5'>定时器</b><b class='flag-5'>作用</b>及<b class='flag-5'>实现</b><b class='flag-5'>定时器</b><b class='flag-5'>数据结构</b><b class='flag-5'>选取</b><b class='flag-5'>介绍</b>1

    什么是软件定时器?软件定时器实现原理

    软件定时器是用程序模拟出来的定时器,可以由一个硬件定时器模拟出成千上万个软件定时器,这样程序在需要使用较多定时器的时候就不会受限于硬件资源的
    的头像 发表于 05-23 17:05 1857次阅读

    定时器实现数据结构选择

    在后端的开发中,定时器有很广泛的应用。 比如: 心跳检测 倒计时 游戏开发的技能冷却 redis的键值的有效期等等,都会使用到定时器定时器实现
    的头像 发表于 11-13 14:22 263次阅读
    <b class='flag-5'>定时器</b>的<b class='flag-5'>实现</b><b class='flag-5'>数据结构</b>选择