Executor 与 Future、await 关系浅析

📅 2026/7/24 4:04:25 👁️ 阅读次数 📝 编程学习
Executor 与 Future、await 关系浅析

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()时,内部ComputeFuturepoll被调用:

  • 如果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 负责。