我正在使用Abortable crate 来暂停 Future 的执行。假设我有一个可中止的 future ,其中异步函数本身正在等待其他异步函数。我的问题是,如果我中止根 Future,子 Future 会同时立即中止,还是会悬空?
我阅读了 Abortable 的源代码,特别是 try_poll
的代码:
fn try_poll<I>(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
poll: impl Fn(Pin<&mut T>, &mut Context<'_>) -> Poll<I>,
) -> Poll<Result<I, Aborted>> {
// Check if the task has been aborted
if self.is_aborted() {
return Poll::Ready(Err(Aborted));
}
// attempt to complete the task
if let Poll::Ready(x) = poll(self.as_mut().project().task, cx) {
return Poll::Ready(Ok(x));
}
// Register to receive a wakeup if the task is aborted in the future
self.inner.waker.register(cx.waker());
// Check to see if the task was aborted between the first check and
// registration.
// Checking with `is_aborted` which uses `Relaxed` is sufficient because
// `register` introduces an `AcqRel` barrier.
if self.is_aborted() {
return Poll::Ready(Err(Aborted));
}
Poll::Pending
}
我的理解是,一旦abort
被调用,它就会传播到下游Futures,从某种意义上说,当根Future被中止时,它将停止轮询其子Future(因为Poll::Ready(Err(Aborted))
将被返回),这将依次停止轮询其子级。如果这个推理是正确的,那么调用 abort 的效果是立即的。
另一个论点是,如果 Future 是基于拉动的,则应首先调用根节点,然后传播到子任务,直到调用并中止叶节点(然后返回到根)。这意味着在调用 abort 方法和叶子 Future 实际停止轮询之间存在延迟。可能相关,但是这个blogpost提到悬空任务,我担心情况就是如此。
例如,这是我写的一个玩具示例:
use futures::future::{AbortHandle, Abortable};
use tokio::{time::sleep};
use std::{time::{Duration, SystemTime}};
/*
* main
* \
* child
* | \
* | \
* leaf1 leaf2
*/
async fn leaf2() {
println!("This will not be printed")
}
async fn leaf1(s: String) {
println!("[{:?}] ====== in a ======", SystemTime::now());
for i in 0..100000 {
println!("[{:?}] before sleep i is {}", SystemTime::now(), i);
sleep(Duration::from_millis(1)).await;
println!("[{:?}] {}! i is {}", SystemTime::now(), s.clone(), i);
}
}
async fn child(s: String) {
println!("[{:?}] ====== in child ======", SystemTime::now());
leaf1(s.clone()).await;
leaf2().await
}
#[tokio::main]
async fn main() {
let (abort_handle, abort_registration) = AbortHandle::new_pair();
let result_fut = Abortable::new(child(String::from("Hello")), abort_registration);
tokio::spawn(async move {
println!("{:?} ^^^^^ before sleep ^^^^^", SystemTime::now());
sleep(Duration::from_millis(100)).await;
println!("{:?} ^^^^^ after sleep, about to abort ^^^^^", SystemTime::now());
abort_handle.abort();
println!("{:?} ***** operation aborted *****", SystemTime::now());
});
println!("{:?} ====== before main sleeps ======", SystemTime::now());
sleep(Duration::from_millis(5)).await;
println!("{:?} ====== after main wakes up from sleep and now getting results \
======", SystemTime::now());
result_fut.await.unwrap();
}
Rust playground
我个人更倾向于第一个论点,即根的中止和叶子的中止之间没有延迟,因为叶子不需要知道它需要中止(叶子仅在根告诉它时才拉动)。上面的示例打印子进程执行的时间和根进程中止的时间。 child 的执行总是在 root 被中止之前,但是我不确定这是否可以证明我的第一个论点是正确的,所以我想知道你们的想法!
最佳答案
是的,因为 future 需要轮询才能执行,但如果中止则不会被轮询,所以子 future 也不会被轮询,因此执行将立即停止。
当然,只有在到达下一个屈服点后执行才会停止,并且使用 tokio::spawn()
生成的任务不会停止。
关于rust - 可中止 : Dangling Futures?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75211452/