手机游戏中显示电子计时器器如何实现

游戏中处处都有定时器基本上烸个逻辑部分我们都能看到定时器的影子。如果翻看一下以前网上流传的一些MMO的代码比如mangos的,比如大唐的比如天龙的,我们都可以看箌形形色色的定时器实现 在以前,很多程序员用起来C++还都是在用C with Object以前的C++写callback也好异步也好总是感觉哪里不对劲的样子,所以网上流传的那种线上服务器的代码一般都是往主循环里硬塞定时器逻辑。 定时器在很多能参考到的代码里都是逻辑和底层不做区分的这样就会导致一些问题。 一方面底层的需求是通用性。要通用性的话就必须得在主循环中轮询timeout而不是借助一些更高层级的抽象; 另一方面,上层嘚需求是易用性要易用性的话就必须得用起来方便,而且最好是能原生嵌入在一些常规的异步编程模型中的最不济的,需要我很方便嘚挂callback再高级点,我需要能yield最上层的,能让我在描述一次lasy evaluation的计算中描述WaitForTime语义做future什么的当然更好了。

游戏中处处都有定时器基本上每個逻辑部分我们都能看到定时器的影子。如果翻看一下以前网上流传的一些MMO的代码比如mangos的,比如大唐的比如天龙的,我们都可以看到形形色色的定时器实现

在以前,很多程序员用起来C++还都是在用C with Object以前的C++写callback也好异步也好总是感觉哪里不对劲的样子,所以网上流传的那種线上服务器的代码一般都是往主循环里硬塞定时器逻辑。

定时器在很多能参考到的代码里都是逻辑和底层不做区分的这样就会导致┅些问题。


一方面底层的需求是通用性。要通用性的话就必须得在主循环中轮询timeout而不是借助一些更高层级的抽象;
另一方面,上层的需求是易用性要易用性的话就必须得用起来方便,而且最好是能原生嵌入在一些常规的异步编程模型中的最不济的,需要我很方便的掛callback再高级点,我需要能yield最上层的,能让我在描述一次lasy evaluation的计算中描述WaitForTime语义做future什么的当然更好了。

但是由于之前说到的,很多现成的嘟是底层上层不区分的所以最常见的可能就是利用一种比较挫的观察者模式,比如继承一个Observable之类的东西挂在主循环中。主循环轮询timeouttimeout叻就callback之前注册进来的Observable。写起来真是要多蛋疼有多蛋疼虽然说既照顾了上层,让上层能用callback了算是温饱,也照顾了底层底层写起来也是主循环来做timeout的,但是这样一来就只是一个扩展性非常差的Timer模块了

当然,这篇文章不打算继续纠缠这种形而上的设计问题上层的一些更高层次的抽象也不是这篇文章的重点,这里重点care下底层定时器机制的实现

一般比较常见的定时器实现,其实就那么几种
一种是比较容噫能想到的,一个简单的最小堆每次tick都查一下top的expire有没有timeout,timeout了就取出来取出来再重复。

这种模型好处就是简单找个学过数据结构的毕業生就能写出来,不容易有bug但是有个比较致命的问题就是,如果短期内注册了大量timer我add的时候需要nlgn,timeout的时候还需要nlgn

所以网上后来就出現了铺天盖地的另一种定时器实现,当然这内核里一坨坨的代码我估计是没人想看的,不重要的细枝末节把我们需要学习的精华地方完铨遮住了。或者看下这里的还是比较浅显易懂的,可读性也很强

这篇文章就重点来对比下这两种定时器的实现。下面代码都上C#了

苐一种。基于最小堆实现的首先你要有一个最小堆,动手实现一下

ps.增加这个Callback主要是为了方便跑测试用例

具体的实现就不用多说了。

然後是第二种第二种思考方式需要有这样一个前提:


通过tick来定义整个系统的时间精度下限。比如游戏中其实都不是特别care 10ms以下的精度的我們可以定义一个tick的长度为10ms。也就是说我先挂上去的WaitFor(8ms)和后上去的WaitFor(5ms)有可能是前者先timeout的。一个tick为10ms那么一个32bit的tick能表达的时间粒度就有将近500天,遠超过一个服务器组不重启的时间了
如果有了这样的前提,就可以针对之前提到的、方法一面对大量临近tick的timer插入锁遇到的问题做一些特殊的优化。
也就是根据tick直接拿到timeout链表直接dispatch,拿到这个链表的时间是一个常数而最小堆方法拿到这个链表需要的时间是m*lgn。

当然由于涳间有限,我们不可能做到每个将要timeout的tick都有对应的链表考虑到其实80%以上的timer的时间都不会超过2.55s,我们只针对前256个tick做这种优化措施即可

那紸册进来一个256tick之后timeout的timer怎么办呢?我们可以把时间还比较长的timer放在更粗粒度的链表中等到还剩下的tick数小于256之后再把他们取出来重新整理一丅链表就能搞定。

如果我们保证每一次tick都严格的做到:

  • 未来256tick内的链表都能常数时间取到
  • 新加入的256tick以及更迟的timer才会加入到粗粒度链表

保证这兩点就需要每个tick都对所有链表做一次整理。这样就得不偿失了所以这里有个trade-off,就是我通过一个指针(index)来标记我当前处理的position,每过256tick是一個cycle才进行一次整理。而整理的成本就通过均摊在256tick中降低了实际上的单位时间成本。

概念比较抽象先来看下数据结构。

循环不变式保證near表具有这样几个性质:


由于原理都类似我这里拿9到14bit的表来说下循环不变式:


有了数据结构和循环不变式,后面的代码也就容易理解了主要列一下AddTimer的逻辑和Shift逻辑。

以上代码用c/c++重写后品尝风味更佳

下面我们来测一下到底linux内核风格的定时器比最小堆的快了多少。

先是构造測试用例我这里只考虑突然的来一大批timer,然后看所有都timeout需要消耗多久

first固定为一千万,这个也是比较符合实际的情况大量的timer都是2.5s以内嘚。可以看出随着远timer数量的增加linux内核定时器对比最小堆定时器的优势是越来越小的。

这个是固定远timer的数量系数固定为1000。跟上图得到的結论差不多近timer占比越高,相比最小堆定时器的优势越大

总之,linux内核定时器比起最小堆定时器的优势还是很明显的随便就能有2倍以上嘚性能表现,强烈建议采用

去年刚来工作室的时候做了个skynet的源码阅读分享,当时也提到了里面定时器的实现但是只看代码那肯定是记鈈住的,总得写一遍后来也一直没抽出时间。直到前几天看到正好业余做的一个小东西开始需要时间模块了,就实现了下顺便产出此小品文。

最新的代码放在了github上:

这个项目是基于本文提到的定时器做了一个unity风格的coroutine附带了测试用例。可以直接把代码抠出来拿来用到項目里

}

我们的时间!只是一个游戏时钟随机定时器。指定的时间范围和启动时钟玩家可以有多个定时器。完美的跟踪导通时间和游戏时间喜欢的朋友千万不要错过,快点來下载吧

  • 秀桌面_单手折腰的女生

}

欢迎来到爱采购百度旗下B2B平台!

全部结果 为您找到 31手机桌面秒表电子计时器器 相关产品信息

卡通 时尚 田园 休闲 美式

北京 上海 杭州 广州

北京 天津 河北 山西 内蒙古 辽宁 吉林 黑龙江 上海 江苏 浙江 安徽 福建 江西 山东 河南 湖北 湖南 广东 广西 海南 重庆 四川 贵州 云南 西藏 陕西 甘肃 青海 宁夏 新疆 台湾 香港

互联网药品信息服务资格证书(京)-经营性- 医疗器械网络交易服务第三方平台备案:(京)网械平台备字(2020) 第00002号

}

我要回帖

更多关于 电子计时器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信