rust 关于.await 的疑惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
youngPacce
V2EX    Rust

rust 关于.await 的疑惑

  •  
  •   youngPacce 2024-05-20 16:00:54 +08:00 2287 次点击
    这是一个创建于 509 天前的主题,其中的信息可能已经有所发展或是发生改变。

    佬们,最近在学习 rust 异步编程,rust 圣经里面有这一句话: <font color=blue>总之,在 async fn 函数中使用.await 可以等待另一个异步调用的完成。但是与 block_on 不同,.await 并不会阻塞当前的线程,而是异步的等待 Future A 的完成,在等待的过程中,该线程还可以继续执行其它的 Future B ,最终实现了并发处理的效果。</font>

    然后我就试了一下下面的这个用例,我预想的结果是 kitty 和 snoopy 先输出,等待 5 秒后再输出 world,但是结果是先输出 kitty,5s 后输出 snoopy,再 5s 输出 world,和文章中描述的好像不太对,请问这个 await 需要怎么理解?

    use futures::executor::block_on; use std::{thread, time}; async fn hello_world() { hello_cat().await; hello_dog().await; println!("hello, world!"); } async fn hello_cat() { println!("hello, kitty!"); let ten_millis = time::Duration::from_secs(5); thread::sleep(ten_millis); } async fn hello_dog() { println!("hello, snoopy!"); let ten_millis = time::Duration::from_secs(5); thread::sleep(ten_millis); } fn main() { let future = hello_world(); block_on(future); } 
    14 条回复    2024-06-14 16:37:06 +08:00
    aggron
        1
    aggron  
       2024-05-20 16:36:27 +08:00
    thread::sleep 是阻塞的,要用 async 版本 sleep. async_std::task::sleep(xx).await / tokio::time::sleep(xx).await
    fcfangcc
        2
    fcfangcc  
       2024-05-20 16:37:39 +08:00
    1 楼说得对,thread::sleep 的问题
    youngPacce
        3
    youngPacce  
    OP
       2024-05-20 16:39:22 +08:00
    @aggron 谢谢我待会试试。
    fcfangcc
        4
    fcfangcc  
       2024-05-20 16:40:45 +08:00
    补充下,即使改了 thread::sleep 也不行,hello_world 需要 join hello_cat 和 hello_dog 两个 future ,不然还是会先运行 hello_cat ,再运行 hello_dog
    raptium
        5
    raptium  
       2024-05-20 16:45:26 +08:00
    只改 sleep 应该也不会是你想要的效果。
    .await 在逻辑上看起来还是阻塞的,只是它不会阻塞系统线程,线程还能去做别的事情。

    需要你的想要效果,cat 和 dog 应该是 spawn 出来跑。这个时候可以再看看 sleep 的效果,如果用了阻塞版 sleep ,那么即使 spawn 了,kitty 和 snoopy 之间也还是要等 5s ,因为 sleep 把线程阻塞了。
    i8086
        6
    i8086  
       2024-05-20 16:46:10 +08:00
    编码方式的改变,语法糖最直观就是用同步写法写异步,而无需写回调。

    操作系统的改变,如:网络请求,同步写法等待响应时会阻塞线程,异步写法等待响应时不会占用线程。

    最大变化在于操作系统那一块,语法糖的便利性方便我们用好异步。
    jonah
        7
    jonah  
       2024-05-20 16:49:37 +08:00
    你可能需要用 https://docs.rs/tokio/latest/tokio/task/fn.spawn.html ,将 future 转成异步执行。
    不然 hello_world 中的 hello_cat 和 hello_dog 两个 future 是顺序 poll 的,一个执行完再执行下一个。
    tootfsg
        8
    tootfsg  
       2024-05-20 17:06:35 +08:00
    这是完全没有基础。
    0ioaths
        9
    0ioaths  
       2024-05-20 17:08:30 +08:00
    将 `hello_cat` 和 `hello_dog` 作为异步任务执行就是期望的效果,以 `tokio runtime` 为例

    ```rust
    use tokio::{runtime, time};

    async fn hello_world() {
    tokio::spawn(hello_cat());
    tokio::spawn(hello_dog());

    let five_secs = time::Duration::from_secs(5);
    time::sleep(five_secs).await;
    println!("hello, world!");
    }

    async fn hello_cat() {
    println!("hello, kitty!");

    let three_secs = time::Duration::from_secs(3);
    time::sleep(three_secs).await;
    println!("hello, kitty!2");
    }

    async fn hello_dog() {
    println!("hello, snoopy!");
    }

    fn main(){
    runtime::Builder::new_multi_thread()
    .worker_threads(4)
    .enable_all()
    .build()
    .unwrap()
    .block_on(hello_world());
    }

    ```

    ```shell
    hello, kitty!
    hello, snoopy!
    hello, kitty!2 // after 3 sces
    hello, world! // after 5 secs
    ```
    my3157
        10
    my3157  
       2024-05-20 18:19:48 +08:00
    async runtime 其实你找个最简单的实现, 看一下就知道了, 包括 tokio smol 这些大量的代码在 reactor, 利用 epoll, kqueue, iocp, io_uring 等各个平台上提供的机制吧默认的阻塞 io 实现成 async io, 不包含这部分的话代码量很小, tokio 稍微多一些, 实现也比较复杂, 比如调度和 work stealing, 理解机制的话最简单的可能也就几百行代码, 很好理解的
    nebkad
        11
    nebkad  
       2024-05-20 22:03:42 +08:00   1
    你的疑惑不在于 await ,而是你还没理解 协程( coroutine) 和 线程 (thread) 的运行方式。
    协程是对线程的分时复用,线程是操作系统提供的对 CPU 的分时复用。
    Rust 的 await 是一种协程相关的关键字,你的理解是线程的工作方式。
    viruscamp
        12
    viruscamp  
       2024-05-21 23:10:11 +08:00
    其实异步这里最好用 js 来理解 async 和 await ,js 还是一个典型的单线程 executor , 而你现在用的 futures::executor::block_on 也是单线程 executor .

    想达到你要的效果,要改两点:
    1. 同时发起 hello_cat().join(hello_dog)).await; // 相当于 js 的 Promise.all
    2. sleep 改异步的,比如这个 https://docs.rs/futures-time/latest/futures_time/task/fn.sleep.html

    如果是常用的多线程 executor 的话, thread::sleep 是可以达到你的效果的,但是错误用法。
    viruscamp
        13
    viruscamp  
       2024-05-22 20:15:04 +08:00
    勘误:
    1. 同时发起 join!(hello_cat(), hello_dog()); // 相当于 js 的 Promise.all
    haharich
        14
    haharich  
       2024-06-14 16:37:06 +08:00
    这应该算是异步编程的知识了
    1 、.await 不会阻塞当前线程主要是指当前线程如果此时有其他异步任务( Future )还是可以同时运行其他异步任务,并不会阻塞在只运行当前异步任务中(因为当前异步任务需要时间去完成),从现在的代码看,只有 hello_world()这个一个 Future ,所以体现不出来,假设还有一个 hello_world1()的 Future ,里面等待时间 2s ,然后不使用 block_on()去驱动这两个 Future ,而是使用 tokio 异步库的 join!()去并发驱动这两个 Future 就能看到其中一个在等待的同时可以运行另一个 Future 并输出日志(注意 thread::sleep(ten_millis) 需要换成异步然后 await 的方式,因为它是阻塞的,不然体现不出效果)
    3 、所以在当前的调用栈上,用 .await 就是要等待当前异步任务完成才能执行后面的逻辑,肯定是先输出 kitty ,hello_cat()的 Future 完成后,才会执行 hello_dog 输出 snoopy ,可以简单认为这里说的不阻塞跟当前调用栈没关系
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2738 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 07:38 PVG 15:38 LAX 00:38 JFK 03:38
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86