理解 Futures 与 Tasks
这节主要讲解 Future
trait 以及异步任务如何被调度的.
它们分别属于 std::future
和 std::task
模块, 这两个模块分在后面两节有详细的介绍.
Future
trait
在同步式的代码 (synchronous code) 里调用阻塞函数(blocking function), 会阻塞整个线程;
而在异步式的代码里, Future
会把控制权返还给线程, 这样其它的 Future
就可以运行了.
它位于 std::future
, async fn
会返回一个Future
对象, 它的接口定义如下:
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
通过调用 poll()
方法, 可以将 Future
对象向前推近到新的进度, 尽量接近任务完成状态.
当 Future
对象完成后, 它返回 Poll::Ready(result)
结果; 如果还没完成, 就返回 Poll::Pending
.
然后当 Future
可以推进到新的进度时, 它会调用 Wake::wake()
函数.
当 wake()
被调用后, 运行时会再次调用 Future
的 poll()
方法, 这样就可以更新进度了.
这里, 如果没有 Wake::wake()
方法的话, 每个周期, 运行时都要依次遍历一下所有的 Future
对象,
看看哪个进度有所推进;
引入了 wake()
方法后, 运行时就可以精确地知道在数万个 Future
对象中有哪些的进度是有变化的,
就可以调用它们的 poll()
方法了.
这样的设计可以显著提高运行时的性能.
另外 Future::poll()
方法的第一个参数类型是 Pin<&mut Self>
, 这个用到了内存固定 (memory pinning) 相关的,
后面章节会专门介绍它.
Poll
枚举类
它位于 std::task
, 定义如下:
pub enum Poll<T> {
Ready(T),
Pending,
}
使用 Waker 来通管运行时再次拉取 Future
Wake
trait
Waker
结构体
RawWaker
结构体