题目
使用 async/await 处理简单异步任务
信息
- 类型:问答
- 难度:⭐
考点
async/await 基本用法, Future trait 理解, tokio 运行时基础
快速回答
在 Rust 中处理异步任务的基本步骤:
- 使用
async关键字定义异步函数 - 在函数内部用
await等待其他异步操作完成 - 使用
#[tokio::main]属性标记主函数 - 通过运行时(如 tokio)执行异步函数
示例核心代码:
#[tokio::main]
async fn main() {
let result = fetch_data().await;
println!("{}", result);
}
async fn fetch_data() -> String {
"Data loaded".to_string()
}
## 解析
原理说明
Rust 的异步编程基于 Future trait:
async fn会返回一个实现了Futuretrait 的类型.await会暂停当前任务直到 Future 完成(非阻塞)- 需要运行时(如 tokio)来驱动 Future 的执行
完整代码示例
// 1. 添加依赖到 Cargo.toml
// [dependencies]
// tokio = { version = "1", features = ["full"] }
// 2. 示例代码
use std::time::Duration;
#[tokio::main] // 创建tokio运行时
async fn main() {
println!("程序开始");
// 启动异步任务
let task = tokio::spawn(async {
println!("子任务开始");
tokio::time::sleep(Duration::from_secs(1)).await;
println!("子任务完成");
});
// 并行执行其他操作
fetch_data().await;
// 等待任务完成
task.await.unwrap();
println!("程序结束");
}
// 异步函数定义
async fn fetch_data() -> String {
tokio::time::sleep(Duration::from_millis(500)).await;
println!("数据获取完成");
"Sample data".to_string()
}最佳实践
- 使用
#[tokio::main]简化运行时启动 - 用
tokio::spawn创建并发任务 - 避免在异步函数中执行长时间阻塞操作(应使用
tokio::time::sleep而非std::thread::sleep) - 使用
async/await代替直接操作Futuretrait
常见错误
- 忘记添加
await导致异步操作未执行 - 在非异步函数中使用
.await - 未配置运行时(如缺少
#[tokio::main]) - 混淆
std::thread::sleep(阻塞线程)和tokio::time::sleep(异步等待)
扩展知识
- Future 状态机:编译器将
async代码转换为状态机 - 运行时选择:tokio 是最常用运行时,另有 async-std、smol 等
- 执行器(Executor):负责调度和执行 Future
- Waker 机制:当 Future 可继续执行时通过 Waker 通知执行器