在 run_interval 中运行异步函数并返回结果

问题描述

我需要在 actix::prelude::AsyncContext::run_interval 中运行一个异步函数,但我还需要传入一个结构体成员并返回结果(不是未来)。这是 this question here一个稍微复杂的版本。从下面的评论部分可以看出,我尝试了几种方法,但由于某种原因,所有方法都失败了。

我查看了一些相关资源,包括 the AsyncContext trait 和这些 StackOverflow 问题:34

这是我的示例代码actix 中需要 Cargo.toml crate):

use std::time::Duration;

use actix::{Actor,Arbiter,AsyncContext,Context,System};

struct MyActor {
    id: i32
}

impl MyActor {
    fn new(id: i32) -> Self {
        Self {
            id: id,}
    }

    fn heartbeat(&self,ctx: &mut <Self as Actor>::Context) {
        ctx.run_interval(Duration::from_secs(1),|act,ctx| {
            //lifetime issue
            //let res = 0;
            //Arbiter::spawn(async {
            //    res = two(act.id).await;
            //});
            //future must return `()`
            //let res = Arbiter::spawn(two(act.id));
            //async closures unstable
            //let res = Arbiter::current().exec(async || {
            //    two(act.id).await
            //});
        });
    }
}

impl Actor for MyActor {
    type Context = Context<Self>;

    fn started(&mut self,ctx: &mut Self::Context) {
        self.heartbeat(ctx);
    }
}

// assume functions `one` and `two` live in another module
async fn one(id: i32) -> i32 {
    // assume something is done with id here
    let x = id;
    1
}

async fn two(id: i32) -> i32 {
    let x = id;
    // assume this may call other async functions
    one(x).await;
    2
}

fn main() {
    let mut system = System::new("test");
    system.block_on(async { MyActor::new(10).start() });
    system.run();
}

Rust 版本:

$ rustc --version
rustc 1.50.0 (cb75ad5db 2021-02-10)

解决方法

使用 Arbiter::spawn 会起作用,但问题在于从传递给 Arbiter::spawn 的异步块内部访问的数据。由于您是从 async 块内部访问 act,因此该引用必须比调用 Arbiter::spawn 的闭包存活时间更长。事实上,in 必须有一个 'static 的生命周期,因为异步块产生的未来可能会持续到程序结束。

在这种特定情况下解决此问题的一种方法是移动它:

ctx.run_interval(Duration::from_secs(1),|act,ctx| {
    let id = act.id;
    Arbiter::spawn(async move {
        two(id).await;
    });
});

由于我们使用了 async move,因此 id 变量将被移动到未来,因此在未来运行时将可用。通过首先将其分配给 id,我们实际上是在复制数据,并且将移动的是副本 (id)。

但这可能不是您想要的,如果您想获得一个更通用的解决方案,您可以在其中访问异步函数中的对象。在这种情况下,它会变得有点棘手,如果可能,您可能需要考虑不使用异步函数。如果必须,可能有一个单独的对象包含您需要的数据,用 std::rc::Rc 包围,然后可以将其移动到异步块中,而无需复制基础数据。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...