当异步任务被中止时会发生什么?

9
Rust有可以绑定到Abortable futures的async方法。文档表示,当中止时:
引用:

将立即完成未来,而不会再做任何进一步的进展。

任务拥有的变量是否会被丢弃?如果这些变量实现了drop,那么drop会被调用吗?如果future已经生成了其他futures,那么它们都会被链式中止吗?
例如:在下面的代码段中,我没有看到中止任务的析构函数,但我不知道它是否被调用或发生在另一个线程中,其中print没有显示。
use futures::executor::block_on;
use futures::future::{AbortHandle, Abortable};

struct S {
    i: i32,
}

impl Drop for S {
    fn drop(&mut self) {
        println!("dropping S");
    }
}

async fn f() -> i32 {
    let s = S { i: 42 };
    std::thread::sleep(std::time::Duration::from_secs(2));
    s.i
}

fn main() {
    println!("first test...");
    let (abort_handle, abort_registration) = AbortHandle::new_pair();
    let _ = Abortable::new(f(), abort_registration);
    abort_handle.abort();
    std::thread::sleep(std::time::Duration::from_secs(1));

    println!("second test...");
    let (_, abort_registration) = AbortHandle::new_pair();
    let task = Abortable::new(f(), abort_registration);
    block_on(task).unwrap();
    std::thread::sleep(std::time::Duration::from_secs(1));
}

{{链接1:游乐场}}


2
在异步代码中,不要使用阻塞/线程休眠。为什么Future::select会首先选择睡眠时间更长的future?封装阻塞I/O的最佳方法是什么? - Shepmaster
1
在第一次测试中,您在未安排执行之前就中止了未来,因此它从未启动→它不需要调用析构函数,因为它从未分配splayground - Jmb
1个回答

9
是的,已经创建的值将被删除。
在您的第一个示例中,由 f 返回的 future 从未启动,因此 S 从未被创建。这意味着它不能被丢弃。
在第二个示例中,该值被丢弃。
如果同时运行并中止 future,则更加明显。在这里,我生成了两个并发的 futures:
  1. 创建一个 S 并等待 200ms
  2. 等待 100ms 并中止 future #1
use futures::future::{self, AbortHandle, Abortable};
use std::time::Duration;
use tokio::time;

struct S {
    i: i32,
}

impl S {
    fn new(i: i32) -> Self {
        println!("Creating S {}", i);
        S { i }
    }
}

impl Drop for S {
    fn drop(&mut self) {
        println!("Dropping S {}", self.i);
    }
}

#[tokio::main]
async fn main() {
    let create_s = async {
        let s = S::new(42);
        time::delay_for(Duration::from_millis(200)).await;
        println!("Creating {} done", s.i);
    };
    let (abort_handle, abort_registration) = AbortHandle::new_pair();
    let create_s = Abortable::new(create_s, abort_registration);

    let abort_s = async move {
        time::delay_for(Duration::from_millis(100)).await;
        abort_handle.abort();
    };

    let c = tokio::spawn(create_s);
    let a = tokio::spawn(abort_s);

    let (c, a) = future::join(c, a).await;

    println!("{:?}, {:?}", c, a);
}

Creating S 42
Dropping S 42
Ok(Err(Aborted)), Ok(())

注意,我已经切换到Tokio以便使用time::delay_for,因为在异步函数中永远不应该使用阻塞操作。
另请参见:

如果未来生成了其他未来,它们是否都会在链中中止?

不会的,当你spawn一个future时,它与它所在的位置断开连接。
另请参见:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接