Tokio 非阻塞后台任务导致错误 `self` 具有匿名生命周期 `'_` 但它需要满足 `'static` 生命周期要求

Tokio non blocking background task leads to error `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement

我有一个异步函数,我想在后台 运行。此函数是不使用异步调用的调用层次结构的一部分。

我的调用层次结构如下所示:

struct Handler {}
impl Handler {

  pub async fn handle(&self) {
     /// does some stuff and updates internal caches
  }
}

struct Client_v2 {
  handler: Handler,
  rt: tokio::runtime::Runtime,
}

impl Client_v2 {

  fn do_work(&self) {
    let future = self.handler(handle);
    self.rt.spawn(future); // want this to run in background and not block! 
    // `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
  }
}

据我了解,存在一些问题,客户端可能比其所有者 Client_v2 寿命更长,从而导致此错误。

我应该如何使这一生有效?我是否需要求助于创建线程并将对象移入和移出线程范围?这个函数调用非常频繁。

首先,handler是不可调用的,它是Client_v2的成员结构。您需要通过点运算符访问它:self.handler.handle().

关于实际问题:

Runtime::spawn 要求提供的未来是 'static + Send,即它不能借用数据并且必须可以将其移动到另一个线程。

'static 要求是由于父线程可能在执行 future 的线程之前退出,因此必须确保提供给任务的任何数据至少与任务本身一样长。

它必须 Send 可用,因为 tokio 运行时可以在其线程池的线程之间自由移动 spawned Futures。

你这个错误的相关要求是第一个,Handler::handle产生的future不是'static因为函数借用了self作为共享引用:async fn handle(&self).调用此函数会生成一个 Future + 'a,其中 'a&'a self 的生命周期,即您可以将整个签名写成 fn handle<'a>(&'a self) -> impl Future<Output=()> + 'a + Send,但 Runtime::spawn 需要您 return impl Future<Output=()> + 'static + Send.

为了证明您没有在 handle 中从 &self 借用数据,您可以使用 async {} 块并将 return 类型显式声明为 impl Future<Output = ()> + Send + 'static:

  pub fn handle(&self) -> impl Future<Output = ()> + 'static + Send {
      async {}
  }

如果您确实需要在 async 块中访问来自 &self 的数据,您需要在异步块之外生成它并将其移动到内部 - 否则生成的未来将再次出现违反了 'static 要求。

  pub fn handle(&self) -> impl Future<Output = ()> + 'static + Send {
      let owned_data = self.prepare_data();
      async move {
          let _owned_data = owned_data; // can now work with `owned_data`
      }
  }

替代@sebpuetz . In rust you can wrap your type in Arc 能够共享其状态,因此您可以将其(Arc 的克隆)移动到您需要计算的未来:

struct Client_v2 {
  handler: Handler,
  rt: tokio::runtime::Runtime,
}

impl Client_v2 {

  pub async fn handle(&self) {
  }
  
  fn do_work(self: Arc<Self>) {
    let handler = self.clone();
    let future = async move { handler.handle().await; };
    self.rt.spawn(future);
  }
}

Playground