tokio 官方给了一个完整的例子:手动构建 runtime ,利用 block_on 来运行多个任务。
    tokio 的任务是由 tokio::spawn 之类的函数产生的 JoinHandle 类型,而且是个 Future

    而下面利用 #[tokio::main] 和 await 编写了等价的版本(为了直观对比任务完成的实际顺序和总耗时,我对 sleep 的时间做了一些简化):

    1. use std::time::Instant;
    2. use tokio::time::{sleep, Duration};
    3. #[tokio::main]
    4. async fn main() -> std::io::Result<()> {
    5. let now = Instant::now();
    6. let mut handles = Vec::with_capacity(10);
    7. for i in 0..10 {
    8. handles.push(tokio::spawn(my_bg_task(i)));
    9. }
    10. // Do something time-consuming while the background tasks execute.
    11. std::thread::sleep(Duration::from_millis(120));
    12. println!("Finished time-consuming task.");
    13. // Wait for all of them to complete.
    14. for handle in handles {
    15. handle.await?;
    16. }
    17. println!("总耗时:{} ms", now.elapsed().as_millis());
    18. Ok(())
    19. }
    20. async fn my_bg_task(i: u64) {
    21. let millis = 100;
    22. println!("Task {} sleeping for {} ms.", i, millis);
    23. sleep(Duration::from_millis(millis)).await;
    24. println!("Task {} stopping.", i);
    25. }

    输出结果:

    1. Task 0 sleeping for 100 ms.
    2. Task 1 sleeping for 100 ms.
    3. Task 2 sleeping for 100 ms.
    4. Task 3 sleeping for 100 ms.
    5. Task 4 sleeping for 100 ms.
    6. Task 5 sleeping for 100 ms.
    7. Task 6 sleeping for 100 ms.
    8. Task 7 sleeping for 100 ms.
    9. Task 8 sleeping for 100 ms.
    10. Task 9 sleeping for 100 ms.
    11. Task 9 stopping.
    12. Task 0 stopping.
    13. Task 1 stopping.
    14. Task 2 stopping.
    15. Task 3 stopping.
    16. Task 4 stopping.
    17. Task 5 stopping.
    18. Task 6 stopping.
    19. Task 7 stopping.
    20. Task 8 stopping.
    21. Finished time-consuming task.
    22. 总耗时:120 ms

    如果把主线程的的 sleep 时间改成 100 ms:std::thread::sleep(Duration::from_millis(100));
    则产生下面的结果:

    1. Task 0 sleeping for 100 ms.
    2. Task 1 sleeping for 100 ms.
    3. Task 2 sleeping for 100 ms.
    4. Task 3 sleeping for 100 ms.
    5. Task 4 sleeping for 100 ms.
    6. Task 5 sleeping for 100 ms.
    7. Task 6 sleeping for 100 ms.
    8. Task 7 sleeping for 100 ms.
    9. Task 8 sleeping for 100 ms.
    10. Task 9 sleeping for 100 ms.
    11. Finished time-consuming task.
    12. Task 3 stopping.
    13. Task 0 stopping.
    14. Task 1 stopping.
    15. Task 2 stopping.
    16. Task 9 stopping.
    17. Task 4 stopping.
    18. Task 5 stopping.
    19. Task 6 stopping.
    20. Task 7 stopping.
    21. Task 8 stopping.
    22. 总耗时:103 ms

    可以看到,my_bg_task 实际是异步非阻塞执行的 👍 :

    • 异步:因为每个任务不必等待其结果就可以开始下一个任务,即; ```rust // 异步 Task 0 sleeping for 100 ms. Task 1 sleeping for 100 ms. …

    // 同步 Task 0 sleeping for 100 ms. Task 0 stopping. Task 1 sleeping for 100 ms. Task 1 stopping. …

    1. - 非阻塞:每个任务之间可以快速切换,不必等待其他任务完成才切换,这个例子表现在:
    2. - 任务 0-9 以乱序方式 stop
    3. - `Finished time-consuming task.` `Task x stopping.` 的打印顺序只与任务各自的运行 (sleep) 时间有关,与源代码的声明执行顺序无关。只有任务之间快速切换才能做到这一点。回顾官网的例子:10 个任务的 sleep 时间线性递减 `let millis = 1000 - 50 * i;`),从 6 个任务开始小于主线程 sleep 任务的时间(750 ms),而等待 10 个任务执行的语句 `for handle in handles { ... }` 显然位于 `std::thread::sleep` 之后,所以任务之间非阻塞执行的话,打印结果为 sleep 时间越短的任务先完成,时间越长的任务后完成,总耗时为任务中的最长耗时:
    4. ```rust
    5. Task 0 sleeping for 1000 ms.
    6. Task 1 sleeping for 950 ms.
    7. Task 2 sleeping for 900 ms.
    8. Task 3 sleeping for 850 ms.
    9. Task 4 sleeping for 800 ms.
    10. Task 5 sleeping for 750 ms.
    11. Task 6 sleeping for 700 ms.
    12. Task 7 sleeping for 650 ms.
    13. Task 8 sleeping for 600 ms.
    14. Task 9 sleeping for 550 ms.
    15. Task 9 stopping.
    16. Task 8 stopping.
    17. Task 7 stopping.
    18. Task 6 stopping.
    19. Finished time-consuming task.
    20. Task 5 stopping.
    21. Task 4 stopping.
    22. Task 3 stopping.
    23. Task 2 stopping.
    24. Task 1 stopping.
    25. Task 0 stopping.
    26. 总耗时:1001 ms // 非常完美

    一般情况下,对于 async block/fn 你至少有以下一些做法:

    1. 对 async block/fn 调用 .await 来等待结果;
    2. 对可列举的少数 Future 调用 join! 或者 select! 来同时等待多个结果 或者 等待多个分支的第一个结果;
    3. 对大量 Future 调用 join 或者 select 一类支持传入 Vec / iter 参数类型的函数,比如这个例子中的 for handle in handles { ... } 部分就可以改写成 futures::future::join_all(handles).await;
    4. 把 async block/fn 变成任务,然后调用 Runtime::block_on (等价地,对任务 await)来执行许多任务。

    容易犯的错误是,希望异步非阻塞时,对所有 async block/fn 进行了 await,而没有进行任务化处理(即 把 Future 通过 spwan 函数转化成任务):

    1. use std::time::Instant;
    2. use tokio::time::{sleep, Duration};
    3. #[tokio::main]
    4. async fn main() {
    5. let now = Instant::now();
    6. let mut handles = Vec::with_capacity(10);
    7. for i in 0..10 {
    8. handles.push(my_bg_task(i)); // 没有把 Future 变成任务
    9. }
    10. std::thread::sleep(Duration::from_millis(120));
    11. println!("Finished time-consuming task.");
    12. for handle in handles {
    13. handle.await; // 而且每个 handle 必须执行完才能执行下一个 handle
    14. }
    15. println!("总耗时:{} ms", now.elapsed().as_millis());
    16. }
    17. async fn my_bg_task(i: u64) {
    18. let millis = 100;
    19. println!("Task {} sleeping for {} ms.", i, millis);
    20. sleep(Duration::from_millis(millis)).await;
    21. println!("Task {} stopping.", i);
    22. }

    运行结果:同步阻塞

    1. Finished time-consuming task.
    2. Task 0 sleeping for 100 ms.
    3. Task 0 stopping.
    4. Task 1 sleeping for 100 ms.
    5. Task 1 stopping.
    6. Task 2 sleeping for 100 ms.
    7. Task 2 stopping.
    8. Task 3 sleeping for 100 ms.
    9. Task 3 stopping.
    10. Task 4 sleeping for 100 ms.
    11. Task 4 stopping.
    12. Task 5 sleeping for 100 ms.
    13. Task 5 stopping.
    14. Task 6 sleeping for 100 ms.
    15. Task 6 stopping.
    16. Task 7 sleeping for 100 ms.
    17. Task 7 stopping.
    18. Task 8 sleeping for 100 ms.
    19. Task 8 stopping.
    20. Task 9 sleeping for 100 ms.
    21. Task 9 stopping.
    22. 总耗时:1130 ms

    或者像这样:

    1. use std::time::Instant;
    2. use tokio::time::{sleep, Duration};
    3. #[tokio::main]
    4. async fn main() {
    5. let now = Instant::now();
    6. let mut handles = Vec::with_capacity(10);
    7. for i in 0..10 {
    8. handles.push(my_bg_task(i)); // 没有把 Future 变成任务
    9. }
    10. std::thread::sleep(Duration::from_millis(120));
    11. println!("Finished time-consuming task.");
    12. futures::future::join_all(handles).await; // 但是 join_all 会等待所有 Future 并发执行完
    13. println!("总耗时:{} ms", now.elapsed().as_millis());
    14. }
    15. async fn my_bg_task(i: u64) {
    16. let millis = 100;
    17. println!("Task {} sleeping for {} ms.", i, millis);
    18. sleep(Duration::from_millis(millis)).await;
    19. println!("Task {} stopping.", i);
    20. }

    运行结果:异步阻塞

    1. Finished time-consuming task.
    2. Task 0 sleeping for 100 ms.
    3. Task 1 sleeping for 100 ms.
    4. Task 2 sleeping for 100 ms.
    5. Task 3 sleeping for 100 ms.
    6. Task 4 sleeping for 100 ms.
    7. Task 5 sleeping for 100 ms.
    8. Task 6 sleeping for 100 ms.
    9. Task 7 sleeping for 100 ms.
    10. Task 8 sleeping for 100 ms.
    11. Task 9 sleeping for 100 ms.
    12. Task 0 stopping.
    13. Task 1 stopping.
    14. Task 2 stopping.
    15. Task 3 stopping.
    16. Task 4 stopping.
    17. Task 5 stopping.
    18. Task 6 stopping.
    19. Task 7 stopping.
    20. Task 8 stopping.
    21. Task 9 stopping.
    22. 总耗时:221 ms

    P.S. 关于代码中 _std::thread::sleep__tokio::time::sleep_ 的区别,参考这篇文章 Async: What is blocking? (by Alice Ryhl)