Rust 的 async/await
语法是编写异步代码的核心工具,它允许我们用类似同步代码的风格编写高性能的非阻塞代码。
1. 核心概念
- 异步编程:在等待 I/O 操作(网络、文件读写等)时让出 CPU,避免阻塞线程。
Future
trait :所有异步操作的基础,代表一个可能尚未完成的计算(类似 JavaScript 的Promise
)。- 执行器 (Executor) :调度和执行异步任务(如
tokio
、async-std
)。
2. 基本语法
(1) 定义异步函数
rust
// 使用 `async fn` 定义异步函数
async fn fetch_data() -> String {
// 模拟异步操作(如网络请求)
"Hello, async!".to_string()
}
- 返回值:
async fn
返回一个实现了Future<Output = String>
的类型(而非直接返回String
)。
(2) 等待异步结果:.await
rust
async fn process() {
// 使用 .await 等待异步操作完成(不阻塞线程)
let data = fetch_data().await;
println!("{}", data); // 输出: "Hello, async!"
}
.await
只能在async
函数/块中使用。- 执行到
.await
时,若Future
未完成,会让出控制权给执行器。
3. 异步代码的执行
步骤 1:创建异步运行时
rust
#[tokio::main] // 使用 tokio 运行时
async fn main() {
process().await;
}
- 需要第三方运行时(如
tokio
或async-std
)驱动Future
的执行。
步骤 2:执行器工作流程
- 调用
process()
返回一个未执行的Future
。 .await
触发Future
的轮询(poll
)。- 若
Future
阻塞(如等待网络),执行器挂起任务并执行其他任务。 - 事件就绪时(如收到网络响应),执行器唤醒任务继续执行。
4. 关键特性
(1) 零成本抽象
- Rust 的
async/await
在编译时转换为状态机,几乎没有运行时开销。
(2) 无堆内存分配(可选)
- 简单
async
函数可在栈上分配状态,避免堆分配(如no_std
环境)。
(3) 协作式调度
-
任务通过
.await
主动让出控制权,需避免长时间阻塞:rust// 错误:阻塞线程的操作! async fn bad_example() { std::thread::sleep(Duration::from_secs(2)); // 阻塞整个线程 } // 正确:使用非阻塞等待 async fn good_example() { tokio::time::sleep(Duration::from_secs(2)).await; // 让出控制权 }
5. 组合异步任务
(1) 并发执行
rust
use tokio::join;
async fn task_one() { /* ... */ }
async fn task_two() { /* ... */ }
async fn run_concurrent() {
// 同时执行两个任务,等待全部完成
let (result1, result2) = join!(task_one(), task_two());
}
(2) 选择最先完成的任务
rust
use tokio::select;
async fn race_tasks() {
select! {
_ = task_one() => println!("Task 1 won"),
_ = task_two() => println!("Task 2 won"),
}
}
6. 错误处理
- 与同步代码一致,使用
Result
和?
:
rust
async fn fetch() -> Result<String, reqwest::Error> {
let resp = reqwest::get("https://example.com").await?;
resp.text().await
}
7. 底层原理:状态机转换
编译后,async
函数被转换为状态机:
rust
// 伪代码:fetch_data() 编译后的状态机
enum FetchDataFuture {
Start,
Waiting(NetworkRequest),
Done,
}
impl Future for FetchDataFuture {
fn poll(&mut self) -> Poll<String> {
match self {
Start => {
let request = start_network_request();
*self = Waiting(request);
Poll::Pending // 首次轮询未完成
}
Waiting(request) => {
if request.is_ready() {
let data = request.get_data();
*self = Done;
Poll::Ready(data) // 完成时返回结果
} else {
Poll::Pending // 继续等待
}
}
Done => panic!("Future already completed"),
}
}
}
总结
特性 | 说明 |
---|---|
async fn |
定义异步函数,返回 Future |
.await |
等待 Future 完成(非阻塞) |
执行器 (Executor) | 驱动 Future 运行(如 tokio ) |
零成本抽象 | 编译为高效状态机,无额外运行时开销 |
协作式调度 | 任务需主动让出 CPU,避免长阻塞操作 |
通过 async/await
,Rust 在保证安全性和性能的同时,提供了直观的异步编程体验。