10.5 async/await 入门:Future 与异步运行时
听
Rust 的并发模型不仅支持基于操作系统线程的并行执行,还提供了高效的异步编程能力,适用于 I/O 密集型任务(如网络请求、文件读写)。通过 async/await 语法和 Future trait,Rust 能在单线程中并发处理大量 I/O 操作,避免线程阻塞带来的资源浪费。
Future 与异步函数
在 Rust 中,异步函数返回一个 Future,它代表一个尚未完成的计算。调用异步函数不会立即执行,而是返回一个可被调度执行的 Future 对象:
async fn fetch_data() -> String {
// 模拟网络延迟
"data".to_string()
}
要真正执行这个函数,必须在一个异步运行时(async runtime)中 .await 它:
#[tokio::main]
async fn main() {
let result = fetch_data().await;
println!("{}", result);
}
await 会挂起当前异步任务,直到 Future 完成,期间运行时可调度其他任务执行,从而实现并发。
异步运行时:tokio 与 async-std
Rust 标准库不包含异步运行时,需依赖外部 crate。目前主流选择有:
- tokio:功能全面,性能优异,广泛用于生产环境;
- async-std:API 设计更贴近标准库,学习曲线平缓。
以 tokio 为例,添加依赖:
[dependencies]
tokio = { version = "1", features = ["full"] }
然后使用 #[tokio::main] 属性将 main 函数标记为异步入口:
#[tokio::main]
async fn main() {
// 异步代码
}
并发执行多个异步任务
使用 tokio::spawn 可并发启动多个异步任务:
use tokio;
#[tokio::main]
async fn main() {
let task1 = tokio::spawn(async {
println!("Task 1 start");
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
println!("Task 1 done");
});
let task2 = tokio::spawn(async {
println!("Task 2 start");
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
println!("Task 2 done");
});
task1.await.unwrap();
task2.await.unwrap();
}
输出顺序可能交错,表明任务并发执行。
异步与同步代码的界限
- 异步函数只能在异步上下文中调用(即另一个
async函数内或运行时中); - 阻塞操作(如
thread::sleep)不应在异步任务中使用,应改用异步等价物(如tokio::time::sleep),否则会阻塞整个运行时线程; async不等于“多线程”——默认情况下,tokio 使用单线程调度器(可通过配置启用多线程)。
与传统线程模型的对比
| 特性 | 线程(std::thread) |
异步(async/await) |
|---|---|---|
| 资源开销 | 每线程 MB 级栈空间 | 每任务 KB 级堆内存 |
| 上下文切换 | 操作系统调度,成本高 | 运行时协作式调度,成本低 |
| 适用场景 | CPU 密集型、并行计算 | I/O 密集型、高并发连接 |
| 编程模型 | 显式线程管理 | 类似同步代码的异步逻辑 |
小结
async/await 是 Rust 处理高并发 I/O 的核心机制。它通过 Future 和运行时调度,在保持代码可读性的同时实现高效资源利用。虽然需要引入外部运行时(如 tokio),但其生态成熟、性能卓越。掌握异步编程,是构建现代网络服务、Web 后端和高性能客户端应用的关键技能。后续深入可探索流(Stream)、异步通道(tokio::sync::mpsc)和异步锁等高级特性。
#Rust 入门教程
分享于 1 周前