tokio::spawn与async块的使用和不使用

3
我正在学习Rust中的async/await,并且我正在按照tokio的教程进行学习。在教程中,有一件事情让我困扰,但是教程似乎没有解释。请参考这个链接:tutorial
tokio::spawn(async move {
    process(socket).await;
});

这是教程中的写法,但我不明白为什么在这里使用了async move {}块。根据我目前的理解,这个异步块是不必要的,也就是说,下面的写法完全可以,并且更加简洁直接:
tokio::spawn(process(socket));

所以,我的问题是:
  1. 在这段代码中,async块是否必要?
  2. 如果是的话,它有什么区别?
  3. 没有编译器优化的情况下,async块会不会增加额外的间接层,可能导致性能略微下降?
2个回答

5
  1. 这段代码中的async块是否必要?

大多数情况下,不需要;但请参考下一个答案。

  1. 如果必要的话,会有什么区别?

如果process()被定义为async fn,那就没有区别。一些人更喜欢其中一种形式,但这是主观的。

然而,如果process()被定义为返回Future的常规函数,那就有区别了:使用async块,全部process()将在新生成的任务中执行,而没有async块,则调用process()会在父任务中执行,只有它返回的future会在生成的任务中执行。

这很重要,例如,如果process()返回的未来不是'static(或Send),那么没有async块的调用将失败。或者如果process()本身可能很昂贵,并且我们希望它在另一个任务中执行,而不仅仅是返回的未来。
需要注意的是,在调用{{link1:Runtime::block_on()}}时也有类似的区别,但在那里这个区别更加重要:没有async块,process()中的代码将在异步运行时之外执行。这意味着对需要活动运行时的函数的调用(例如tokio::spawn())将失败。
  1. 没有编译器优化,异步块会增加额外的间接层,可能会导致性能略微下降吗?
我不清楚生成器(以及在生成器之上实现的异步函数和块)的具体实现细节,但这是不太可能的。Rust中的异步块不会分配内存,而且我认为仅仅使用一个转发的async块不会产生任何额外开销。

嗨Chayim,谢谢回复。所以,如果process是一个异步函数,那么spawn(process()) / block_on(process())等同于spawn(async { process().await }) / block_on(async { process().await })。然而,如果process是一个返回Future的普通函数,那么这两者就有所不同。这个理解正确吗? - techhara
@techhara 是的,是有可能有区别的:可能所有这个功能做的就只是返回未来,并且它本身并不进行任何工作;即使它有工作,也可能在任务中执行或在父级中执行并不重要。但是确实可能存在区别。 - Chayim Friedman

1
除了@ChayimFriedman的答案之外,`async move {}`中的`move`部分可能很重要。
在所示代码中不一定需要,但在类似情况下,如果`async`函数期望一个引用,那么为了塑造一个具有所有权的`Future`,可能需要使用`move`。
以下是一个演示,直接的方法会触发生命周期错误,而`async`块由于使用了`move`而不会触发错误:
async fn process(_: &i32) {}

fn main() {
    let n = 5;
    
    tokio::spawn(process(&n));
    
    tokio::spawn(async move {
        process(&n).await
    });
}

error[E0597]: `n` does not live long enough
  --> src/main.rs:6:26
   |
4  |     let n = 5;
   |         - binding `n` declared here
5  |     
6  |     tokio::spawn(process(&n));
   |                  --------^^-
   |                  |       |
   |                  |       borrowed value does not live long enough
   |                  argument requires that `n` is borrowed for `'static`
...
11 | }
   | - `n` dropped here while still borrowed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接