Что происходит с задачей asyn c, когда она прерывается? - PullRequest
3 голосов
/ 06 января 2020

Rust имеет async методы, которые можно привязать к фьючерсам Abortable. В документации сказано, что при прерывании:

будущее завершится немедленно, без дальнейшего прогресса.

Будут ли отброшены переменные, принадлежащие задаче, связанной с будущим ? Если эти переменные реализуют drop, будет ли вызываться drop? Если будущее породило другие фьючерсы, все ли они будут прерваны в цепочке?

Например: В следующем фрагменте я не вижу деструктора происходит для прерванной задачи, но я не знаю, вызывается ли она или происходит в отдельном потоке, где 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 Ответ

5 голосов
/ 06 января 2020

Да, значения, которые были созданы , будут отброшены.

В вашем первом примере будущее, возвращаемое 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, поскольку вы никогда не должны использовать блокирующие операции в асинхронной функции c.

См. Также:

Если будущее породит другие фьючерсы, все ли они прервутся в цепочке?

Нет, когда вы spawn a будущее, оно отключено от того места, где оно было создано.

См. также:

...