Rust定时组件async-rs / futures-timer实现原理

Posted Sean

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust定时组件async-rs / futures-timer实现原理相关的知识,希望对你有一定的参考价值。

项目中需要使用定时器,看了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...

以上是关于Rust定时组件async-rs / futures-timer实现原理的主要内容,如果未能解决你的问题,请参考以下文章

Rust编程语言里的future

指定 Rust 闭包的生命周期

# Rust异步网络编程

从 rust 中的通道迭代器获取第一个接收到的值

rust异步之asyncawaitfuture

rust异步之asyncawaitfuture