簡體   English   中英

在實踐中實現 Future 時如何使用 Context 和 Wakers

[英]How to use Context and Wakers when implementing Future in practice

我發現很難理解為什么以及何時需要對傳遞給 object 上的poll方法的Context和/或其Waker顯式執行某些操作,我正在為其實現Future 我一直在閱讀TokioAsync Book的文檔,但我覺得示例/方法太抽象而無法應用於實際問題。

例如,我會認為下面的 MRE 會死鎖,因為new_inner_task生成的未來不知道消息何時在 MPSC 通道上傳遞,但是,這個例子似乎工作正常。 為什么會這樣?

use std::{future::Future, pin::Pin, task::{Context, Poll}, time::Duration};
use futures::{FutureExt, StreamExt}; // 0.3
use tokio::sync::mpsc; // 1.2
use tokio_stream::wrappers::UnboundedReceiverStream; // 0.1

async fn new_inner_task(rx: mpsc::UnboundedReceiver<()>) {
    let mut requests = UnboundedReceiverStream::new(rx);
    while let Some(_) = requests.next().await {
        eprintln!("received request");
    }
}

pub struct ActiveObject(Pin<Box<dyn Future<Output = ()> + Send>>);

impl ActiveObject {
    pub fn new() -> (Self, mpsc::UnboundedSender<()>) {
        let (tx, rx) = mpsc::unbounded_channel();
        (Self(new_inner_task(rx).boxed()), tx)
    }
}

impl Future for ActiveObject {
    type Output = ();
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        eprintln!("[polled]");
        self.get_mut().0.as_mut().poll(cx)
    }
}

async fn delayed_send(delay: u64, sender: mpsc::UnboundedSender<()>) {
    tokio::time::sleep(Duration::from_millis(delay)).await;
    sender.send(()).unwrap();
    eprintln!("sent request");
}

#[tokio::main]
async fn main() {
    let (obj, tx) = ActiveObject::new();
    let ds = delayed_send(500, tx.clone());
    let ds2 = delayed_send(1000, tx);
    
    tokio::join!(obj, ds, ds2);
}

我從本地運行此示例獲得的 output 是:

[polled]
[polled]
sent request
[polled]
received request
[polled]
sent request
[polled]
received request

因此,雖然我沒有對ContextWaker做任何事情,但ActiveObject似乎以合理的速度被輪詢,也就是說,比要求的頻率更高,但不是忙於等待。 是什么導致ActiveObject以這種速度被喚醒/輪詢?

您正在將相同的Context (因此Waker )傳遞給由new_inner_task返回的 Future 的poll()方法,該方法將其沿鏈傳遞到由UnboundedReceiverStream::next()返回的Futurepoll() ) 。 其實現安排在適當的時間(當新元素出現在通道中時)在此Waker上調用wake() )。 完成后,Tokio 會輪詢與此Waker關聯的頂級 future - 三個 future 的join!()

如果您省略了輪詢內部任務的行並只返回Poll::Pending ,您將得到預期的情況,您的Future將被輪詢一次,然后永遠“掛起”,因為沒有什么會再次喚醒它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM