如何定义从类生成的 tokio 任务的生存期?



我正在尝试编写一个通用的set_interval函数助手:

pub fn set_interval<F, Fut>(mut f: F, dur: Duration)
where
F: Send + 'static + FnMut() -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
let mut interval = tokio::time::interval(dur);
tokio::spawn(async move {
// first tick is at 0ms
interval.tick().await;
loop {
interval.tick().await;
tokio::spawn(f());
}
});
}

这工作正常,直到从类内部调用它:

fn main() {}
struct Foo {}
impl Foo {
fn bar(&self) {
set_interval(|| self.task(), Duration::from_millis(1000));
}

async fn task(&self) {

}
}

self不是'static,我们不能因为tokio::task而将生命周期参数限制为小于'static的东西。

是否可以修改set_interval实现,使其在这种情况下正常工作?

<小时 />

链接到游乐场

<小时 />

附言

试图
let instance = self.clone();
set_interval(move || instance.task(), Duration::from_millis(1000));

但我也收到一个错误:错误:捕获的变量无法FnMut闭包正文中转义

是否可以

修改set_interval实现,使其在这种情况下工作?

没有。尽管spawn-ingf()实际上也无济于事,因为它排除了简单的"回调拥有对象"解决方案(因为您需要回调和 future 来拥有对象,或者只是未来)。

我认为这留下了两个解决方案:

  1. 将所有内容转换为共享可变性Arc,回调拥有一个Arc,然后在每次报价时克隆该克隆并将克隆移动到未来(task方法)。
  2. 让未来(task)从某个外部源获取对象,而不是在一个外部源上调用,这样中间回调就不需要做任何事情。或者回调可以进行获取并将其移动到未来,相同的差异。

顺便说一下,在这一点上,直接创造未来是有意义的,但允许克隆它。因此,与其进行回调set_interval不如采用可克隆的未来,它将spawn()其存储未来的克隆,而不是重新创建它们。

正如@Masklinn所提到的,你可以克隆 Arc来允许这样做。请注意,克隆 Arc不会克隆底层数据,只会克隆指针,因此通常可以这样做,并且不会对性能产生重大影响。

下面是一个示例。以下代码将生成错误async block may outlive the current function, but it borrows data, which is owned by the current function

fn main() {
//    Error: async block may outlive the current function, but it borrows data, which is owned by the current function
let data = Arc::new("Hello, World".to_string());
tokio::task::spawn(async {
println!("1: {}", data.len());
});
tokio::task::spawn(async {
println!("2: {}", data.len());
});
}

Rust 无助地建议将move添加到两个异步块中,但这会导致借用错误,因为会有多个所有权。

为了解决这个问题,我们可以为每个任务克隆 Arc,然后将move关键字添加到异步块中:

fn main() {
let data = Arc::new("Hello, World".to_string());
let data_for_task_1 = data.clone();
tokio::task::spawn(async move {
println!("1: {}", data_for_task_1.len());
});

let data_for_task_2 = data.clone();
tokio::task::spawn(async move {
println!("2: {}", data_for_task_2.len());
});
}

最新更新