游戏服务器一般都会需要这样一种功能 – 在某个确定的时间点干某件特定的事情,例如:某个排行需要在每月第一天进行结算,某个比赛需要在下周六中午12:00准时通知服务器所有玩家比赛开始……当然,这种功能不局限于游戏服务器,其他类型服务器或多或少也有包含。我们称之这个功能为定时器功能或者定时器组件。

这里实现了一种用C++模板机制实现的秒级(比较容易改写成毫秒级)定时器,核心算法使用的是时间轮(Timing-Wheel),辅助数据结构使用的是侵入式链表(Intrusive-List)。首先简单介绍一些基本概念。

侵入式链表(Intrusive-List)

最典型的Intrusive-List例子应该就是linux内核中使用的链表了,它的优点是用户知道自己在表中的位置,删除比较方便,且用户自己管理数据块,这个数据块可以被多个链表同时共享(注意这是一个很重要的特性,相比非侵入式链表节约了不少空间),这种数据结构在redis,nginx代码中广泛使用。

相对应的,非侵入式链表(Non-intrusive-list)的例子就是std::list了,被使用在各大软件与应用中,易用,稳定,用户群里大基本任何关于它的问题都可以google到,当然这里说的Non-intrusive-list不单单只是指std::list,只是std::list占去了Non-intrusive-list至少90%的“市场份额”。

一些比较好的关于链表资料:

时间轮(Timing-Wheel)

对于Timing-Wheel的基本理解就像老式钟表,一个圆盘,上面很多刻度,外加刻度针。每次Tick,轮盘转动(根据物理相对运动,也可以理解成刻度针摆动),这时指针指向新的刻度标签,这个刻度标签上有任何过期定时器那么则进行处理,定期进行下一次Tick,处理下一个刻度标签上的事件……

本文的实现

游戏服务器一般来说不需要很精确的计时(端游上需要毫秒级,一些技能的吟唱是需要毫秒级支持的),特别手游服务器一般精确到秒级即可,所以对于Timing-Wheel的N取值为60即可(1分钟60秒,每秒对应一个轮)。我们采用每个轮子上挂接顺序链表的方式进行定时器存储,这样的话在轮询时只用判断表头是否过期即可。

对于插入行为,我们根据定时器过期时间(expireTm)对N值进行求余hash,从而找到对应的轮,将其插入顺序链表。

对于轮的转动,原则上以秒为单位进行指针移动。

问题与解决

问题1:若定时器过期处理函数(func)耗时过长,导致精度下降,如何处理?

建议是在func中不要放过重的逻辑,如果无法避免重逻辑,可以使用线程解决。但是使用线程会使开发同步性的成本就会增加,也会增加维护的复杂度,所以这是一个取舍过程,本文中的实现无法完全避免这个问题。

问题2:一个过期处理函数(func)耗时几秒,已经导致了精确严重下降,如何处理?

本文实现一个简单的追赶机制,我们在处理定时器过期线程(滴答线程)将每次滴答(Tick)时间默认设置为200ms,并且记录当前的时间指针位置(secStep),当发生了一个很耗时的func,我们会发现当前时间(curTm)大于secStep,那么我将secStep++,直到curTm==secStep我们就不做任何移动当前secStep的操作,让其自动随时间移动,这样在1秒的时间内可以大约追赶4秒误差。

问题3:定时器是否线程安全?

是,粗暴的使用线程锁。

问题4:定时器内存管理是怎么样的?

内存会对定时器事件(timerEvent),进行new/delete(可以优化为高效的内存池模式),无需用户进行额外的内存操作。

java实现时间轮定时器_基于侵入式链表的时间轮定时器实现相关推荐

  1. 侵入式与非侵入式链表

    侵入式与非侵入式链表 非侵入式链表 非侵入式链表中数据保存在节点中,结点链接域存的是下一个结点的内存首地址 template<typename T> struct ListNode {Li ...

  2. java线程轮询_基于springboot实现轮询线程自动执行任务

    本文使用: Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.使用这种方式可以让你的程序按照某一个频度执行, 但不能在指定 ...

  3. java 长轮询_基于springboot 长轮询的实现操作

    springboot 长轮询实现 基于 @EnableAsync , @Sync @SpringBootApplication @EnableAsync public class DemoApplic ...

  4. java象棋游戏用户特点_基于Java Swing的《中国象棋》游戏的设计与实现

    60 开发经验 3基金项目: 江西省自然科学基金资助项目(编号: 0411046); 江西省高性能计算技术重点实验室资助基金项目(No. JXHC20052003) ). 基于 Java Swing ...

  5. java契约_基于契约式设计的Java编译器实现

    上海交通大学硕士学位论文 基于契约式设计的Java编译器实现 学校:上海交通大学 院系:软件学院 专业:计算机软件与理论 班级:B0403791 学号:1040379006 作者姓名:张嘉铭 指导教师 ...

  6. stm32单片机实现多个闹钟_基于STM32F103系列单片机的11个定时器解析

    STM32F103系列的单片机一共有11个定时器,其中: 2个高级定时器 4个普通定时器 2个基本定时器 2个看门狗定时器 1个系统嘀嗒定时器 除去看门狗定时器和系统滴答定时器的八个定时器列表; 8个 ...

  7. pde与波长 sipm 关系_基于SiPM和TCMPC的时间分辨拉曼散射测量技术研究

    引用本文 苗泉龙, 代雷, 李佰成, 赵天琦, 梁琨, 杨茹, 韩德俊. 基于SiPM和TCMPC的时间分辨拉曼散射测量技术研究[J]. 光谱学与光谱分析, 2018,38(5): 1444-1450 ...

  8. mysql中时间处理函数_基于mysql时间处理函数的应用详解

    DAYOFWEEK(date) 返回日期date的星期索引(1=星期天,2=星期一, --7=星期六).这些索引值对应于ODBC标准. mysql> select DAYOFWEEK('1998 ...

  9. java 非侵入式_非侵入式设计 和侵入式设计 意思?

    非侵入式系介绍DI用语,我得理解是两个组件(类,接口)之间,比较独立,不深入到另一个类内部,哪位大虾能点拨一二? 关于"侵入式"和"非侵入式"设计 有读者讲&q ...

  10. vuejs 轮播_如何在VueJS中设计和构建轮播功能

    vuejs 轮播 by Fabian Hinsenkamp 由Fabian Hinsenkamp设计 A carousel, slideshow, or slider - however you ca ...

最新文章

  1. ArrayList用法说明
  2. Scala与Java差异(五)之Map与Tuple
  3. mysql 自连接 树形_自连接表的相关问题(树形结构)
  4. Eclipse 常用快捷键收集
  5. 生成式建模“回归”信息抽取
  6. 转:js中arguments详解
  7. 7.Java反射面试题
  8. Multisim简体中文汉化包下载安装指南
  9. Python学习笔记——python基础之python中for......else......的使用
  10. 每周全球科技十大新闻(2021.2.1-2.7)
  11. 面试 11、知识拓展
  12. 在线Base64编码/解码
  13. Python制作词云视频,通过词云图来看小姐姐跳舞
  14. python之bug0:selenium使用新版edge(chrome内核) 导致的webdriver.Edge 运行报错
  15. 修改mysql8.0中数据库的名字
  16. 阿里巴巴十年Java架构师分享,会了这个知识点的人都去BAT了
  17. Conflux项目进度报告 十月第一期
  18. linux cadaver 命令,screen命令用法与cadaver
  19. 打开PS出现Unable to continue because of a hardware quo
  20. 设置vlan虚拟局域网

热门文章

  1. ISSCC2021 基于SRAM的存内计算16.3阅读记录
  2. 开源有限元程序AsFem
  3. 概要设计和详细设计(软件)
  4. 基于电能计量芯片HLW8012的应用研究
  5. 使用jemeter进行接口压力测试
  6. UDS知识整理(二):UDS诊断服务简介
  7. libcrypto.so.1.0.0编译
  8. linux查看网口位置命令,Linux下查看网卡信息及确定网卡位置
  9. chrome axure 插件安装
  10. 交叉编译工具链下载地址