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
运行时可以在其线程池的线程之间自由移动 spawn
ed Future
s。
你这个错误的相关要求是第一个,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);
}
}
我有一个异步函数,我想在后台 运行。此函数是不使用异步调用的调用层次结构的一部分。
我的调用层次结构如下所示:
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
运行时可以在其线程池的线程之间自由移动 spawn
ed Future
s。
你这个错误的相关要求是第一个,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 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);
}
}