异步Rust中的每个Future不同的上下文或唤醒者

时间:2019-11-22 14:04:33

标签: rust async-await future polling

我试图了解轮询在Async Rust Future中如何工作。使用以下代码,我尝试运行两个期货Fut0Fut1,以使它们像在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)吗?

1 个答案:

答案 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期货仍然使用相同的上下文)。