共计 1534 个字符,预计需要花费 4 分钟才能阅读完成。
我的项目中须要应用定时器,看了 Rust 官网提供的 futures-timer 的源码,将实现原理这里记录下来。
以 async-rs / futures-timer v3.0.0 源码为根底。
很多同学是 java 开发,因而与 java 的 Timer 比照,不便理解(待补充)。
另外本文不蕴含任何源码,这样不须要懂得 Rust 语言也能了解其中的设计原理。想看源码的同学,置信浏览本文当前再去看源码应该高深莫测。
组成构造
rust
数据结构蕴含 Delay、Heap、ArcList、Timer。
其中,Delay 是用户定义的一个提早工作,但仅蕴含延迟时间,没有设置工作来回调。Heap 是一个最小堆,每个节点就是 Delay,依据 Delay 的工夫作为优先级,每次获取工夫最近的 Delay。ArcList 是一个队列,其中的对象也是 Delay。Timer 是总体的定时器,封装了 Heap 和 ArcList。
管制组件包含 waker(待补充)
实现原理
插入工作:
用户创立 Delay 当前,写入 ArcList 后,由 timer 将 Delay 迁徙到 Heap 中,最终 Delay 从 Heap 堆顶取出,实现整个生命周期。另外,ArcList 反对并发写入,Heap 不反对并发,这里 ArcList 起到一个音讯队列的作用。
注册回调:
Delay 工作插入 Timer,也就是插入 ArcList 当前,同时也将 Delay 注册到监听器 waker 中,Waker 是 Timer 中的一个监听器。当 Delay 工作在 Heap 中超时时,也就是从堆顶取出时,Waker 触发 Delay 工作。这里的触发其实是一个 future 返回后果,Delay 实现了 future 接口,当 Delay 注册到 Timer 当前,用户期待 Delay 的 future 实现,等 Delay 被触发时,future 返回后果,以此达到定时成果。
回调告诉
Timer 中有一个监听线程解决 Heap 堆里的 Delay 的超时,流程是取出堆顶的 Delay 当前,计算下一个 Delay 的工夫,而后 sleep。另外,当有新 Delay 插入时,也会唤醒这个线程,从新判断是否有 Delay 曾经超时,而后再次 sleep。
其余性能实现原理
工夫重置
当批改之前曾经存在的 Delay 的执行工夫时,之前写入的 Delay 应该有效。采纳的办法是,在 Delay 中减少两个计数字段,一个是当初重置过几次 t,一个是工作写入 Heap 时 t 的值。当重置 Delay 时,将新生成一个 Delay,两个计数字段都是 t +1,插入 ArcList,并且会批改 Heap 中原来的 Delay,第一个计数字段变成 t +1,而第二个字段还是 t。当原来的 Delay 超时时,两个字段不统一,主动疏忽。
注意事项
官网提供 async-rs / futures-timer 组件。须要留神的是,async-rs / futures-timer 在版本 0.0.6 当前将 Interval 性能删除了,Interval 性能指的是固定周期定时工作性能,只保留了 Delay 性能,Delay 性能指的是执行一次的工作。尽管 Interval 性能是对 Delay 做了一层封装 Delay 性能,然而用户手动实现还是有肯定复杂度。
tokio 也提供 tokio::time 组件,反对 Delay 性能、Interval 性能、以及 TimeOut 性能,TimeOut 性能指的是如果一个 Future 在指定工夫内没有返回后果就超时。须要留神的是 tokio::time 只在[“time”]feature 上反对。
参考
- https://github.com/async-rs/f…
- https://zhuanlan.zhihu.com/p/…
- https://wiki.jikexueyuan.com/…
- https://docs.rs/tokio/1.6.1/t…