Executor 可以想象成 Future 的调度器吗?
是的,非常准确。
Executor(执行器)就是负责管理和轮询(poll)Future 的调度系统。
它维护一个就绪任务队列,当一个 Future 返回 Poll::Pending 时,它会注册一个唤醒器(Waker),然后当该 Future 就绪(如 IO 完成)时,Waker 会通知 Executor,Executor 会将其重新放入队列并再次 poll。
Executor 与 Future 的关系
| 角色 | 职责 |
|---|---|
| Future | 定义了异步计算的状态机,有一个poll方法,由 Executor 调用。它本身是惰性的,不执行任何操作,除非被 poll。 |
| Executor | 驱动 Future 运行的外部引擎。它负责:1. 在合适的时机调用Future::poll2. 处理 Waker 通知 3. 管理任务的生命周期(如多线程调度) |
关系总结:Future 是被动的“计算任务”,Executor 是主动的“任务驱动器”。两者通过 Waker 进行协作。
Executor 与await是否功能重合?
不重合,它们处于完全不同的层次。
| 概念 | 层面 | 作用 |
|---|---|---|
await | 语法糖(语言特性) | 在.await点挂起当前任务,将控制权交还给 Executor,并等待 Future 完成。它不调度任务,只负责“等待”。 |
| Executor | 运行时组件(库提供) | 实际调度和执行 Future,管理线程池或事件循环,驱动所有异步任务向前推进。 |
类比:Executor 是“发动机”,Future 是“变速器”,而await是“离合器”——它让发动机和变速器暂时分离,但不负责驱动车辆。
详细示例(带计算过程的异步操作)
模拟“费时计算 + 网络请求”来展示这三者的协作。
usetokio::time::{sleep,Duration};usestd::future::Future;usestd::pin::Pin;usestd::task::{Context,Poll};// 1. 定义一个自定义 Future:模拟耗时计算structComputeFuture{count:u32,}implFutureforComputeFuture{typeOutput=u32;fnpoll(mutself:Pin<&mutSelf>,cx:&mutContext<'_>)->Poll<Self::Output>{println!("poll: count = {}",self.count);ifself.count>=10{Poll::Ready(self.count)}else{self.count+=1;// 模拟异步等待:注册一个定时器 Wakerletwaker=cx.waker().clone();tokio::spawn(asyncmove{sleep(Duration::from_millis(100)).await;waker.wake();// 通知 executor 再次 poll});Poll::Pending}}}// 2. 一个异步函数,使用 awaitasyncfnasync_compute()->u32{letresult=ComputeFuture{count:0}.await;// 这里挂起,等待完成result*2}// 3. 另一个异步函数,模拟网络请求asyncfnfetch_data()->u32{sleep(Duration::from_millis(50)).await;// 模拟 IO42}#[tokio::main]// tokio 的 executor 在此asyncfnmain(){// 并发执行两个任务let(computed,fetched)=tokio::join!(async_compute(),fetch_data());println!("computed: {}, fetched: {}",computed,fetched);}执行流程
#[tokio::main]创建了一个 Executor(多线程工作窃取调度器)。
tokio::join!将两个 Future 提交给 Executor,Executor 开始轮询它们。
当轮询async_compute()时,内部ComputeFuture的poll被调用:
- 如果
count < 10,它注册一个 Waker(通过waker.wake()),并返回Poll::Pending。 - 此时,
await关键字将当前任务挂起,控制权返回给 Executor。
Executor 发现任务挂起,就去轮询fetch_data(),它同样可能挂起(sleep)。
当定时器触发时,Waker 被调用,Executor 重新将ComputeFuture放入队列,再次 poll,直到count == 10,返回Poll::Ready。
此时await得到结果,继续执行result * 2。
所有任务完成,join!返回结果。
三者分工示意(伪代码)
Executor: while 有任务: 选择一个任务 调用 Future::poll(task) 如果返回 Poll::Pending: 保存任务,等待 Waker 否则: 完成并返回结果 Future::poll: 执行一步计算 如果未完成: 注册 Waker 返回 Poll::Pending 否则: 返回 Poll::Ready(value) await: 调用 Future::poll 如果返回 Poll::Pending: 挂起当前 async 函数,保存栈状态 将控制权交还给 Executor 否则: 获取值并继续执行总结
| 概念 | 角色 | 是否驱动执行? | 是否触发调度? |
|---|---|---|---|
| Future | 状态机 | 否(被动被 poll) | 否 |
| Executor | 调度器 | 是(驱动 poll) | 是(管理任务队列) |
| await | 等待语法 | 否(只是挂起) | 否(只是让出控制权) |
关键点:await 并不调度任务,它只是将控制权交还给 Executor,让 Executor 去调度其他任务。真正的调度和并发执行完全由 Executor 负责。