RustAsync/Await实战:从串行到并发,掌握block_on与join!的异步魔力Rust以其零成本抽象和内存安全特性在系统编程领域备受推崇。在构建高性能网络服务或处理高并发任务时,理解和运用其异步编程模型至关重要。与Go、Node.js等语言提供“开箱即用”的异步方案
block_on 与 join! 的异步魔力Rust 以其零成本抽象和内存安全特性在系统编程领域备受推崇。在构建高性能网络服务或处理高并发任务时,理解和运用其异步编程模型至关重要。与 Go、Node.js 等语言提供“开箱即用”的异步方案不同,Rust 采取了更通用的方式,提供了 Future 和 async/await 这样的基础模块。本文将深入浅出地对比系统线程与异步模型的差异,并通过一系列实战代码示例,重点讲解如何使用 futures::executor::block_on 和 futures::join! 等工具,从最基础的串行调用逐步迈向高效的并发执行,解锁 Rust 并发模型的强大威力。
由操作系统管理,抢占式多任务(preemptively multi-tasked)
Async Model,协作式多任务(cooperatively multi-tasked)
| 场景 | 使用系统线程 | 使用 Async |
|---|---|---|
| 任务运行时间 | 长时间运行的任务 | 短时间运行的任务 |
| 任务类型 | CPU 密集型任务 | I/O 密集型任务 |
| 并行性需求 | 需要真正并行运行的任务 | 需要并发运行的任务 |
| 延迟需求 | 需要最小且可预测的延迟 | 能利用等待时间来做其他事,提升吞吐量的任务 |
最好的做法是:将两者结合使用,这样才能真正发挥 Rust 并发模型的威力。
use futures::executor::block_on;
async fn hi() {
println!("Hello, world!");
}
// 1. async fn 可以执行 non-async fn
// 2. non-async fn 不可以执行 async fn,除非有 executor
fn main() {
let func = hi(); // executor
block_on(func);
}
这段 Rust 代码展示了 异步函数 (async fn) 的基本使用和如何在 非异步函数 (non-async fn) 中执行它。hi 函数被声明为 async fn,这意味着它返回一个 Future。Future 表示一个可能还没有完成的异步操作。非异步函数,例如 main 函数,不能直接调用并等待一个 async fn 完成,因为它需要一个 执行器 (executor) 来驱动 Future 的执行。代码中使用的 use futures::executor::block_on; 引入了 block_on 这个执行器。在 main 函数中,let func = hi(); 实际上只是创建了一个 Future(即那个异步操作的定义),并没有开始执行。然后,block_on(func); 充当了阻塞式的执行器,它会阻塞当前的线程(即 main 函数)直到 hi() 返回的 Future 完成,从而输出 "Hello, world!"。简而言之,这段代码演示了如何使用 block_on 库中的工具在同步的 main 函数中同步地运行一个异步函数。
➜ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/async_rust`
Hello, world!
这段运行结果表明 Rust 程序已成功编译和执行。具体来说,cargo run 命令首先完成了代码的编译(Finished 'dev' profile...),这个过程非常快,只用了 $0.01$ 秒。然后,它执行了生成的调试版本程序(Running 'target/debug/async_rust'),程序的执行结果输出了 "Hello, world!"。这个输出是程序中 async fn hi() 内部的 println! 宏在 main 函数通过 block_on(func) 被成功驱动执行后产生的。这证明了异步函数被执行器正确地运行。
use futures::executor::block_on;
async fn hi() {
println!("Hello, world!");
hello_rust().await;
}
// 1. async fn 可以执行 non-async fn
// 2. non-async fn 不可以执行 async fn,除非有 executor
async fn hello_rust() {
println!("Hello, Rust!");
}
fn main() {
let func = hi(); // executor
block_on(func);
}
这段 Rust 代码进一步展示了异步函数 (async fn) 之间的串联调用。hi 函数和 hello_rust 函数都被定义为 async fn。在 hi 函数内部,它首先打印 "Hello, world!",然后使用 .await 关键字来暂停自身的执行,直到另一个异步函数 hello_rust() 完成。hello_rust() 只打印 "Hello, Rust!"。由于 hi() 内部有 .await,它会等待 hello_rust() 运行完毕才会继续。最后,在同步的 main 函数中,block_on(func) 充当执行器,阻塞地驱动整个异步操作 (hi() 及其内部调用的 hello_rust()) 运行直到完成,因此程序会按顺序输出两条信息。这段代码的核心在于说明 async fn 之间可以通过 .await 实现非阻塞的等待和协作,而 block_on 则是将整个异步...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!