在异步上下文中,我觉得编译器应该知道这一点,并允许我在方法内部调用.await,但无论是否在运行时上下文中,在同步函数内部基本上不可能使用await。await会被转换为yield points,并且异步函数将被转换为状态机来利用这些yield points执行异步计算。如果没有将函数标记为async,则无法进行此转换。如果我正确理解了你的问题,你有以下代码:
#[tokio::main]
async fn main() {
let foo = Foo {};
foo.bar()
}
impl Trait for Foo {
fn bar(df: DataFrame) -> Vec<Data> {
df.collect().await
}
}
问题在于您不能从
bar
中等待
df.collect
,因为它没有被标记为
async
。如果您可以修改
Trait
的签名,那么您可以使用
如何在Trait中定义异步方法?中提到的解决方法,使
Trait::bar
成为一个异步方法。
如果您无法更改
Trait
的签名,则会出现问题。异步函数永远不应花费太长时间而不达到
.await
。正如
如何封装非异步代码中的阻塞I/O最佳方法中解释的那样,您可以在转换为非异步代码时使用
spawn_blocking
:
#[tokio::main]
async fn main() {
let foo = Foo {};
tokio::task::spawn_blocking(move || foo.bar()).await
}
impl Trait for Foo {
fn bar(df: DataFrame) -> Vec<Data> {
df.collect().await
}
}
现在你需要一种方法来执行
df.collect
,而不需要等待完成。你提到你尝试创建嵌套运行时来解决这个问题:
"If I try and block_on the future from a new runtime ... I get a panic"
然而,tokio不允许您创建嵌套的运行时。您可以创建一个新的独立运行时,如
How can I create a Tokio runtime inside another Tokio runtime所述。但是,生成嵌套的运行时将效率低下。
相反,您可以获得对
当前运行时的句柄:
let handle = Handle::current();
输入运行时上下文:
handle.enter();
然后使用futures::executor::block_on
将未来任务运行至完成:
impl Trait for Foo {
fn bar(df: DataFrame) -> Vec<Data> {
let handle = Handle::current();
handle.enter();
futures::executor::block_on(df.collect())
}
}
进入Tokio运行时上下文将解决您先前遇到的错误:
```
如果我尝试futures :: executor :: block_on(df.collect())。unwrap();,我会得到一个新的运行时panic “not currently running on a Tokio 0.2.x runtime”
```
如果可以,请尽量避免这样做。最佳解决方案是将`Trait :: bar`标记为`async`并将`await`视为正常操作。包括上述提到的任何其他解决方案,都涉及阻塞当前线程直到给定的future完成。
Credit @AliceRyhl for the explanation
awaiting
根本不起作用。你可以使用tokio::spawn_blocking
来生成一个阻塞任务。 - Ibraheem Ahmedspawn_blocking
,我会查一下并更新。谢谢! - growseJoinHandle
,仍需要await
-ed - 除非我漏掉了什么? - growse