我试图了解轮询在Async Rust Future中如何工作。使用以下代码,我尝试运行两个期货Fut0
和Fut1
,以使它们像在Fut0 -> Fut1 -> Fut0 -> Fut0
之后一样交织。
extern crate futures; // 0.3.1
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, Waker};
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashMap;
use futures::executor::block_on;
use futures::future::join_all;
#[derive(Default, Debug)]
struct Fut {
id: usize,
step: usize,
wakers: Rc<RefCell<HashMap<usize, Waker>>>,
}
impl Future for Fut {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.step += 1;
println!("Fut{} at step {}", self.id, self.step);
{
let mut wakers = self.wakers.borrow_mut();
wakers.insert(self.id, cx.waker().clone());
}
{
let next_id = (self.id + self.step) % 2;
let wakers = self.wakers.borrow();
if let Some(w) = wakers.get(&next_id) {
println!("Waking up Fut{} from Fut{}", next_id, self.id);
w.wake_by_ref();
}
}
if self.step > 1 {
Poll::Ready(())
} else {
Poll::Pending
}
}
}
macro_rules! create_fut {
($i:ident, $e:expr, $w:expr) => (
let $i = Fut {
id: $e,
step: 0,
wakers: $w.clone(),
};
)
}
fn main() {
let wakers = Rc::new(RefCell::new(HashMap::new()));
create_fut!(fut0, 0, wakers);
create_fut!(fut1, 1, wakers);
block_on(join_all(vec![fut0, fut1]));
}
但是它们总是以轮询方式(即Fut0 -> Fut1 -> Fut0 -> Fut1 -> ...
)进行轮询。
Fut0 at step 1
Fut1 at step 1
Waking up Fut0 from Fut1
Fut0 at step 2
Waking up Fut0 from Fut0
Fut1 at step 2
Waking up Fut1 from Fut1
似乎,它们的所有上下文都是相同的,因此每个期货的Wakers也相同。因此,唤醒其中一个唤醒另一个。每个未来都有可能拥有不同的Context(或Waker)吗?
答案 0 :(得分:1)
方法futures::future::join_all
返回的Future会按顺序而不是并行地轮询给定的Future。您应该这样看,期货是嵌套的,执行者将仅引用计划的最高期货(在这种情况下,futures::future::join_all
返回的期货)。
这意味着当对join_all future进行轮询时,它将上下文传递给其当前正在执行的嵌套Future。此后,join_all future将其传递给下一个嵌套的future,依此类推。对所有嵌套的期货有效地使用相同的上下文。可以通过查看期货箱中的JoinAll
future的源代码来验证这一点。
block_on
执行程序当时只能执行一个未来。使用线程池的执行程序(例如tokio)实际上可以并行执行期货,因此将对不同的预定的期货使用不同的上下文(但由于上述原因,对于JoinAll期货仍然使用相同的上下文)。>