在Run_interval中运行async函数并返回结果



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

我已经查看了一些相关的资源,包括AsyncContext特性和这些StackOverflow问题:3,4。

这是我的示例代码(Cargo.toml中需要actix机箱(:

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的异步块内部访问数据。由于您是从异步块内部访问act,因此该引用的生存时间必须比调用Arbiter::spawn的闭包更长。事实上,In的生存期必须为"static",因为异步块产生的未来可能会一直持续到程序结束。

在这种特定情况下,考虑到异步块中需要一个i32,并且i32是Copy类型,解决这个问题的一种方法是移动它:

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包围,然后可以将其移动到异步块中,而无需复制底层数据。

最新更新