< 返回版块

heliping 发表于 2022-11-04 13:56

异步调用,必须await才能触发? 那我加了await,必须等待当前的任务完成才能继续往下走? 那不就是单任务吗? 新手比较迷惑。

评论区

写评论
liuchang0920 2022-11-18 03:11

await只是个语法糖,其实是通过一个executor去执行一些列的future (async function)。

await保证了在一个function里面的逻辑是能够保证先后顺序的,但是对于多个async task来说他们可以是并发的 (如果是单线程的thread的话那么他们还是串行的,不过会因为异步的原因会yield 把cpu让出来给其他的available task来执行,以此来提高cpu的利用率),如果能够跑在多核上的话理论上是可以提高效率的。

这里有个example: https://rust-lang.github.io/async-book/02_execution/04_executor.html#:~:text=Future%20executors%20take%20a%20set,future%20once%20to%20start%20off.可以了解下他的实现

作者 heliping 2022-11-07 17:31

谢谢各位大神,已解决。 使用tokio::spawn

eweca-d 2022-11-07 10:32

同意Aya0wind的说法,很全面。在rust里,并行貌似全是通过新线程来保证的,详情可以看“https://tokio.rs/tokio/tutorial/spawning”页面,await的机制是保证逻辑上的串行不会被破坏,如Aya0wind说的,request->response这个逻辑顺序是不能被破坏的,所以request之后一定要await它结束才能写response,但是非常多个request->response的逻辑是可以并行的。

而你想要的估计是这样的:

use tokio;

async fn a() {
    println!("enter func a");
    tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
    println!("exit func a");
}

async fn b() {
    println!("enter func b");
    tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
    println!("exit func b");
}

#[tokio::main]
async fn main() {
    // 开新线程异步运行a
    let handle = tokio::spawn(async {
        a().await;
    });

    // 休息1秒钟,以保证a能够先于b运行(注意:因为开新线程需要时间因CPU而异,此处测试代码只是尽量保证a先运行)
    tokio::time::sleep(tokio::time::Duration::from_secs(1));
    
    // 异步运行b
    b().await;

    // 休息1秒钟,b运行3秒钟,总共4秒钟,b先结束

    // 再等待1秒钟,a运行结束(因为显然b结束之后,大概1秒a也结束了,所以是并行的)
    handle.await.unwrap();

    // 打印结果为:
    // enter func a
    // enter func b
    // exit func b
    // exit func a
}
ctaoist 2022-11-07 09:55

op问的应该是类似golang里的 go func(),在rust中可以 tokio::spawn 或者 tokio::scope,后者是新出的,可以借用局部变量

Aya0wind 2022-11-05 13:27

await是让你能让多个连续的,前后相关的任务按顺序执行。
比如一个http服务器,你肯定要read一个request之后之后再write一个response吧,那就意味着这个write必须在read之后。写出来就是这样

let request = read_request().await;
//...
write_response(response).await;

但是这只是在一个子任务中,这个子任务本身的逻辑是没法并发的,因为有数据依赖关系。但是rust支持你开N个这样的任务,也就是上面那段代码其实是有很多并发在一起跑的,你在一个任务里await之后,说明这个任务就没法继续推进了,就可以去执行其他的任务,在服务器上就对应一个服务器程序并发服务N个客户端,当你要等待一个客户端的网络包发过来的时候,就利用这个等待时间转而去继续推进其他任务。这就是意义。光看这一个子任务,那确实是串行的,但是当你子任务await之后,就可以回复执行其他的任务,这些任务从宏观上来看就是并发的。

AndyJado 2022-11-05 11:09

我理解的await: 我要开始包饺子的时候你馅和面皮一定给我准备好喽.

op的await: 等会儿我要包饺子, 你们开始准备皮儿和馅吧, 你别问我什么时候包, 叫你准备你就去准备, 多嘴干什么?

Borber 2022-11-04 14:31

那你的需求就并不是异步而是子线程

--
👇
heliping: js里面的await好理解,本身不加await,其他的任务不会等待,直接从头到尾,然后再去执行await的结果。 这个rust就比较困惑了,比如我 async fn a{ b.await; retrun c }

我期望b启动执行,但不需要结果,先返回c就行了,返回了再继续执行b。 实际不加await,不会执行b; 加了await,又得等到b执行完了才return c。

Cherrs 2022-11-04 14:27

你的要求可以显式使用tokio::spawn来处理

async fn a{ 
tokio::spawn(b);
retrun c;
}

我的理解是直接调用的话不安全

ruby 2022-11-04 14:23

你只会写 async+await 的话那就跟同步函数没啥区别, 也是串行执行没有发挥出异步编程的精髓

真正的精髓在 IO 多路复用,例如 join/select 可以同时 await 多个 Future 达到并行执行的效果

例如你用 reqwest 发网络请求,用 await 写法也是串行执行

for i in 0..5 {
    // 实际上是串行执行,5个请求 one-by-one
    reqwest.get().await;
}

如果你用上 tokio::select 或者其他办法就可以做到让这五个异步请求同时执行

作者 heliping 2022-11-04 14:01

js里面的await好理解,本身不加await,其他的任务不会等待,直接从头到尾,然后再去执行await的结果。 这个rust就比较困惑了,比如我 async fn a{ b.await; retrun c }

我期望b启动执行,但不需要结果,先返回c就行了,返回了再继续执行b。 实际不加await,不会执行b; 加了await,又得等到b执行完了才return c。

1 共 10 条评论, 1 页