heliping 发表于 2022-11-04 13:56
异步调用,必须await才能触发? 那我加了await,必须等待当前的任务完成才能继续往下走? 那不就是单任务吗? 新手比较迷惑。
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.可以了解下他的实现
谢谢各位大神,已解决。 使用tokio::spawn
同意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 }
op问的应该是类似golang里的 go func(),在rust中可以 tokio::spawn 或者 tokio::scope,后者是新出的,可以借用局部变量
go func()
tokio::spawn
tokio::scope
await是让你能让多个连续的,前后相关的任务按顺序执行。 比如一个http服务器,你肯定要read一个request之后之后再write一个response吧,那就意味着这个write必须在read之后。写出来就是这样
let request = read_request().await; //... write_response(response).await;
但是这只是在一个子任务中,这个子任务本身的逻辑是没法并发的,因为有数据依赖关系。但是rust支持你开N个这样的任务,也就是上面那段代码其实是有很多并发在一起跑的,你在一个任务里await之后,说明这个任务就没法继续推进了,就可以去执行其他的任务,在服务器上就对应一个服务器程序并发服务N个客户端,当你要等待一个客户端的网络包发过来的时候,就利用这个等待时间转而去继续推进其他任务。这就是意义。光看这一个子任务,那确实是串行的,但是当你子任务await之后,就可以回复执行其他的任务,这些任务从宏观上来看就是并发的。
我理解的await: 我要开始包饺子的时候你馅和面皮一定给我准备好喽.
op的await: 等会儿我要包饺子, 你们开始准备皮儿和馅吧, 你别问我什么时候包, 叫你准备你就去准备, 多嘴干什么?
那你的需求就并不是异步而是子线程
-- 👇 heliping: js里面的await好理解,本身不加await,其他的任务不会等待,直接从头到尾,然后再去执行await的结果。 这个rust就比较困惑了,比如我 async fn a{ b.await; retrun c }
我期望b启动执行,但不需要结果,先返回c就行了,返回了再继续执行b。 实际不加await,不会执行b; 加了await,又得等到b执行完了才return c。
你的要求可以显式使用tokio::spawn来处理
async fn a{ tokio::spawn(b); retrun c; }
我的理解是直接调用的话不安全
你只会写 async+await 的话那就跟同步函数没啥区别, 也是串行执行没有发挥出异步编程的精髓
真正的精髓在 IO 多路复用,例如 join/select 可以同时 await 多个 Future 达到并行执行的效果
例如你用 reqwest 发网络请求,用 await 写法也是串行执行
for i in 0..5 { // 实际上是串行执行,5个请求 one-by-one reqwest.get().await; }
如果你用上 tokio::select 或者其他办法就可以做到让这五个异步请求同时执行
js里面的await好理解,本身不加await,其他的任务不会等待,直接从头到尾,然后再去执行await的结果。 这个rust就比较困惑了,比如我 async fn a{ b.await; retrun c }
评论区
写评论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.可以了解下他的实现
谢谢各位大神,已解决。 使用tokio::spawn
同意Aya0wind的说法,很全面。在rust里,并行貌似全是通过新线程来保证的,详情可以看“https://tokio.rs/tokio/tutorial/spawning”页面,await的机制是保证逻辑上的串行不会被破坏,如Aya0wind说的,request->response这个逻辑顺序是不能被破坏的,所以request之后一定要await它结束才能写response,但是非常多个request->response的逻辑是可以并行的。
而你想要的估计是这样的:
op问的应该是类似golang里的
go func()
,在rust中可以tokio::spawn
或者tokio::scope
,后者是新出的,可以借用局部变量await是让你能让多个连续的,前后相关的任务按顺序执行。
比如一个http服务器,你肯定要read一个request之后之后再write一个response吧,那就意味着这个write必须在read之后。写出来就是这样
但是这只是在一个子任务中,这个子任务本身的逻辑是没法并发的,因为有数据依赖关系。但是rust支持你开N个这样的任务,也就是上面那段代码其实是有很多并发在一起跑的,你在一个任务里await之后,说明这个任务就没法继续推进了,就可以去执行其他的任务,在服务器上就对应一个服务器程序并发服务N个客户端,当你要等待一个客户端的网络包发过来的时候,就利用这个等待时间转而去继续推进其他任务。这就是意义。光看这一个子任务,那确实是串行的,但是当你子任务await之后,就可以回复执行其他的任务,这些任务从宏观上来看就是并发的。
我理解的await: 我要开始包饺子的时候你馅和面皮一定给我准备好喽.
op的await: 等会儿我要包饺子, 你们开始准备皮儿和馅吧, 你别问我什么时候包, 叫你准备你就去准备, 多嘴干什么?
那你的需求就并不是异步而是子线程
--
👇
heliping: js里面的await好理解,本身不加await,其他的任务不会等待,直接从头到尾,然后再去执行await的结果。 这个rust就比较困惑了,比如我 async fn a{ b.await; retrun c }
我期望b启动执行,但不需要结果,先返回c就行了,返回了再继续执行b。 实际不加await,不会执行b; 加了await,又得等到b执行完了才return c。
你的要求可以显式使用tokio::spawn来处理
我的理解是直接调用的话不安全
你只会写 async+await 的话那就跟同步函数没啥区别, 也是串行执行没有发挥出异步编程的精髓
真正的精髓在 IO 多路复用,例如 join/select 可以同时 await 多个 Future 达到并行执行的效果
例如你用 reqwest 发网络请求,用 await 写法也是串行执行
如果你用上 tokio::select 或者其他办法就可以做到让这五个异步请求同时执行
js里面的await好理解,本身不加await,其他的任务不会等待,直接从头到尾,然后再去执行await的结果。 这个rust就比较困惑了,比如我 async fn a{ b.await; retrun c }
我期望b启动执行,但不需要结果,先返回c就行了,返回了再继续执行b。 实际不加await,不会执行b; 加了await,又得等到b执行完了才return c。