rust - Async Rust 中每个 Future 的不同 Context 或 Waker

标签 rust async-await future polling

我正在尝试了解 Async Rust Future 中轮询的工作原理。使用下面的代码,我尝试运行两个 futures 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

看起来,它们所有的 Context 都是相同的,因此每个 Futures 的 Wakers 也是相同的。所以唤醒其中一个也会唤醒另一个。是否可以为每个 future 设置不同的 Context(或 Waker)?

最佳答案

方法 futures::future::join_all 返回一个 future ,它按顺序而不是并行地轮询给定的 future 。你应该看待它的方式是, future 是嵌套的,执行者只会引用最顶层的 future (在本例中为 futures::future::join_all 返回的 future )。 这意味着当 join_all future 被轮询时,它将上下文传递给它当前正在执行的嵌套 future 。此后 join_all future 将把它传递给下一个嵌套的 future 等等。有效地为所有嵌套的 future 使用相同的上下文。这可以通过查看 JoinAll future 的源代码来验证。在 future 箱子里。

block_on executor 一次只能执行一个 future。使用线程池的执行器(例如 tokio)实际上可以并行执行 future ,因此将为不同的预定 future 使用不同的上下文(但由于上述原因,对于 JoinAll future 仍然是相同的上下文)。

关于rust - Async Rust 中每个 Future 的不同 Context 或 Waker,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58995888/

相关文章:

c# - 我在异步/等待方面做错了什么?

rust - Rust 中的下划线 : "consider using"

stack - 使用内联汇编更改堆栈指针时调用函数会崩溃

function - 如何在Rust中将通用函数指针存储为 `any`?

rust - 使用 hyper 将 block 流异步写入文件

Java-使用 invokeAll 按顺序获取 future 结果,但仅适用于某些线程

scala - Promise.tryComplete中的参数指的是什么?

rust - 如何在Substrate中具有不可变的键值映射?

c# - 如何使用 Async/Await 进行进度报告

javascript - JS async/await - 为什么 await 需要异步?