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

huangapple go评论118阅读模式
英文:

What happens to an async task when it is aborted?

问题

Rust具有可以与Abortable futures关联的async方法。文档中说,当被中止时:

> 该future将立即完成,不再进行任何进一步的进展。

与future绑定的任务拥有的变量会被丢弃吗?如果这些变量实现了dropdrop会被调用吗?如果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));
}

playground

英文:

Rust has async methods that can be tied to Abortable futures. The documentation says that, when aborted:

> the future will complete immediately without making any further progress.

Will the variables owned by the task bound to the future be dropped? If those variables implement drop, will drop be called? If the future has spawned other futures, will all of them be aborted in a chain?

E.g.: In the following snippet, I don't see the destructor happening for the aborted task, but I don't know if it is not called or happens in a separate thread where the print is not shown.

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));
}

playground

答案1

得分: 9

是的,已经创建的值将会被丢弃。

在你的第一个示例中,由f返回的未来从未启动,因此S从未被创建。这意味着它无法被丢弃。

在第二个示例中,该值被丢弃。

如果你同时运行未来并中止它,这会更加明显。在这里,我生成了两个并发的未来:

  1. 创建一个S并等待200毫秒
  2. 等待100毫秒并中止未来#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一个未来时,它与它的生成位置断开连接。

另请参阅:

英文:

Yes, values that have been created will be dropped.

In your first example, the future returned by f is never started, so the S is never created. This means that it cannot be dropped.

In the second example, the value is dropped.

This is more obvious if you both run the future and abort it. Here, I spawn two concurrent futures:

  1. create an S and waits 200ms
  2. wait 100ms and abort 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(())

Note that I've switched to Tokio to be able to use time::delay_for, as you should never use blocking operations in an async function.

See also:

> If the future has spawned other futures, will all of them be aborted in a chain?

No, when you spawn a future, it is disconnected from where it was spawned.

See also:

huangapple
  • 本文由 发表于 2020年1月6日 22:25:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/59613782.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定