本文转载自:https://zhuanlan.zhihu.com/p/78036342
已获作者授权。
之前的文章已经提到Future,Executor和Reactor是rust异步的三个基本组件。由于io相关的Reactor涉及到操作系统的api,分析Future和Reactor 之间的交互过程往往没那么直观。futures-timer是rust官方提供一个纯用户态实现的定时器基础库,本身代码精炼,通过源码的阅读分析,可以学习到:
学习Reactor,以及和Future的交互逻辑;
学习thread::park/unpark的使用;
学习一个简单的并发链表设计;
干净地进行资源的释放;
futures-timer对外提供了Delay和Interval,以及充当Reactor角色的Timer,默认情况下会启动一个后台线程运行全局的Timer,高级用户也可以自己创建管理Timer。借助于Delay支持时间重设的特性,Interval只是Delay的一个封装,因此不做论述。
futures-timer 整体架构图
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/df454646d2d0f2e2e76f791449c7ea48.jpeg)
定时器超时的管理一般有红黑树,最小堆和旋转时间轮等数据结构。futures-timer采用的是最小堆,由Timer结构维护。由于设计一个并发的最小堆难度较大,因此 Timer结构有两部分组成,一个是管理定时器超时的堆数据结构,是Timer的私有数据,不能并发访问;一个是用于和外部交互的并发链表,作为存放外部请求的待处理队列。Timer把并发链表包装成TimerHandle,供外部创建和修改定时器。主要的数据结构定义如下:
pub struct Timer {
inner: Arc<Inner>,
timer_heap: Heap<HeapTimer>,
}
pub struct TimerHandle {
inner: Weak<Inner>,
}
struct Inner {
// 保存刚创建或者更新的定时器队列,等待Timer进行处理
list: ArcList<ScheduledTimer>,
// Timer一般在一个死循环里,没消息时处于睡眠状态,
// 当上面的list更新后通过这个waker把Timer唤醒让其继续干活
waker: AtomicWaker,
}
pub struct Delay {
state: Option<Arc<Node<ScheduledTimer>>>, // 为None时表示定时器无效
when: Instant,
}
/// Delay和Timer之间共享的数据
struct ScheduledTimer {
waker: AtomicWaker, // 用于Timer给Delay发送通知
// 第一个bit标记是否定时器是否已经触发,第二个bit标记是否定时器无效,
// 其他bits标记定时器被重置的次数,每重置一次加1。只有和HeapTimer里保存的一致时才会触发超时通知。
state: AtomicUsize,
inner: Weak<Inner>,
at: Mutex<Option<Instant>>,
slot: Mutex<Option&