Rust Async/Await 异步编程完全指南
Rust 的异步编程基于 Future trait 和 async/await 语法,提供零成本抽象的高性能异步 I/O。
目录
异步编程基础
为什么需要异步编程?
同步 vs 异步
// 同步代码(阻塞)
fn sync_read_file() -> String {
std::fs::read_to_string("file.txt").unwrap()
// 线程阻塞,等待 I/O 完成
}
// 异步代码(非阻塞)
async fn async_read_file() -> String {
tokio::fs::read_to_string("file.txt").await.unwrap()
// 任务挂起,CPU 可以执行其他任务
}
资源对比
| 模型 | 每个连接占用 | 可支持连接数 | 适用场景 |
|---|---|---|---|
| 线程 | ~2MB(线程栈) | 数千 | CPU 密集型 |
| 异步 | ~几KB(任务) | 数万/百万 | I/O 密集型 |
1. 什么是 Future?
Future 是 Rust 异步编程的核心 trait,表示一个异步计算。
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
// Future trait 的简化版本
trait SimpleFuture {
type Output;
fn poll(&mut self, cx: &mut Context) -> Poll<Self::Output>;
}
enum Poll<T> {
Ready(T), // 计算完成
Pending, // 计算未完成,稍后再检查
}
手写 Future 示例
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
struct Delay {
when: Instant,
}
impl Future for Delay {
type Output = String;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if Instant::now() >= self.when {
println!("延迟完成!");
Poll::Ready(String::from("完成"))
} else {
println!("还没到时间,继续等待...");
// 通知执行器稍后再检查
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
// 使用示例
async fn use_delay() {
let delay = Delay {
when: Instant::now() + Duration::from_secs(2),
};
let result = delay.await;
println!("结果: {}", result);
}
2. async/await 语法糖
async 和 await 是编译器提供的语法糖。
// async fn 是语法糖
async fn fetch_data() -> String {
String::from("data")
}
// 等价于返回 impl Future
fn fetch_data_desugared() -> impl Future<Output = String> {
async {
String::from("data")
}
}
// 使用 await
async fn process() {
let data = fetch_data().await;
println!("{}", data);
}
Future 和执行器
1. 执行器的作用
Future 是惰性的,需要执行器来驱动。
use tokio;
#[tokio::main] // 这个宏创建了执行器
async fn main() {
let result = async_task().await;
println!("{}", result);
}
async fn async_task() -> String {
String::from("Hello, Async!")
}
2. 常见执行器
| 执行器 | 特点 | 使用场景 |
|---|---|---|
| tokio | 全功能,多线程 | 通用异步应用 |
| async-std | 类似标准库 API | 学习和轻量应用 |
| smol | 轻量级 | 嵌入式和简单应用 |
| futures | 底层构建块 | 自定义执行器 |
3. 手动轮询 Future
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
fn dummy_waker() -> Waker {
fn no_op(_: *const ()) {}
fn clone(_: *const ()) -> RawWaker {
dummy_raw_waker()
}
fn dummy_raw_waker() -> RawWaker {
let vtable = &RawWakerVTable::new(clone, no_op, no_op, no_op);
RawWaker::new(std::ptr::null(), vtable)
}
unsafe { Waker::from_raw(dummy_raw_waker()) }
}
fn block_on<F: Future>(mut future: F) -> F::Output {
let waker = dummy_waker();
let mut context = Context::from_waker(&waker);
let mut future = unsafe { Pin::new_unchecked(&mut future) };
loop {
match future.as_mut().poll(&mut context) {
Poll::Ready(result) => return result,
Poll::Pending => {
// 简化示例:实际应该让出 CPU
std::thread::sleep(std::time::Duration::from_millis(10));
}
}
}
}
// 使用
fn main() {
let result = block_on(async {
"Hello from custom executor!"
});
println!("{}", result);
}
async/await 语法
1. async 函数
// 基本 async 函数
async fn simple_async() -> i32 {
42
}
// 带参数的 async 函数
async fn greet(name: String) -> String {
format!("Hello, {}!", name)
}
// 泛型 async 函数
async fn identity<T>(value: T) -> T {
value
}
// async 方法
struct User {
id: u64,
}
impl User {
async fn fetch(&self) -> String {
format!("User data for ID: {}", self.id)
}
}
#[tokio::main]
async fn main() {
let result = simple_async().await;
println!("{}", result);
let greeting = greet(String::from("Alice")).await;
println!("{}", greeting);
let user = User { id: 1 };
let data = user.fetch().await;
println!("{}", data);
}
2. async 块
#[tokio::main]
async fn main() {
// async 块
let future = async {
println!("异步块内部");
42
};
let result = future.await;
println!("结果: {}", result);
// 带 move 的 async 块
let data = vec![1, 2, 3];
let future = async move {
println!("数据: {:?}", data);
};
future.await;
// data 已被移动,不能再使用
}
3. await 表达式
#[tokio::main]
async fn main() {
// 基本 await
let result = async_function().await;
// await 可以在表达式中
let sum = async_add(1, 2).await + async_add(3, 4).await;
println!("和: {}", sum);
// await 可以链式调用
let result = fetch_data()
.await
.parse::<i32>()
.unwrap();
// 条件 await
let data = if condition {
async_branch_a().await
} else {
async_branch_b().await
};
}
async fn async_function() -> i32 {
42
}
async fn async_add(a: i32, b: i32) -> i32 {
a + b
}
async fn fetch_data() -> String {
String::from("123")
}
async fn async_branch_a() -> String {
String::from("A")
}
async fn async_branch_b() -> String {
String::from("B")
}
let condition = true;
4. ? 操作符与 await
use std::io;
async fn read_file(path: &str) -> io::Result<String> {
// ? 和 await 结合使用
let content = tokio::fs::read_to_string(path).await?;
Ok(content)
}
async fn process_multiple_files() -> io::Result<()> {
let file1 = read_file("file1.txt").await?;
let file2 = read_file("file2.txt").await?;
println!("文件1: {}", file1);
println!("文件2: {}", file2);
Ok(())
}
Tokio 异步运行时
1. 基本使用
use tokio;
// 方式1:使用宏
#[tokio::main]
async fn main() {
println!("Hello, Tokio!");
}
// 方式2:手动创建运行时
fn main() {
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async {
println!("Hello, Tokio!");
});
}
// 方式3:自定义运行时配置
fn main() {
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.thread_name("my-async-worker")
.enable_all()
.build()
.unwrap();
runtime.block_on(async {
println!("自定义运行时");
});
}
2. tokio::spawn - 生成异步任务
use tokio;
#[tokio::main]
async fn main() {
// 生成异步任务
let handle = tokio::spawn(async {
println!("任务开始");
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
println!("任务完成");
42
});
// 等待任务完成并获取结果
let result = handle.await.unwrap();
println!("结果: {}", result);
}
3. tokio::time - 时间操作
use tokio::time::{sleep, timeout, interval, Duration};
#[tokio::main]
async fn main() {
// 延迟执行
println!("开始延迟");
sleep(Duration::from_secs(1)).await;
println!("延迟结束");
// 超时控制
let result = timeout(Duration::from_secs(2), async {
sleep(Duration::from_secs(3)).await;
"完成"
}).await;
match result {
Ok(value) => println!("成功: {}", value),
Err(_) => println!("超时!"),
}
// 定时器
let mut interval = interval(Duration::from_millis(500));
for i in 0..5 {
interval.tick().await;
println!("滴答 {}", i);
}
}
4. tokio::fs - 异步文件操作
use tokio::fs;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> std::io::Result<()> {
// 读取文件
let content = fs::read_to_string("example.txt").await?;
println!("文件内容: {}", content);
// 写入文件
fs::write("output.txt", b"Hello, Async!").await?;
// 使用 File
let mut file = fs::File::create("data.txt").await?;
file.write_all(b"异步写入的数据").await?;
// 读取文件到缓冲区
let mut file = fs::File::open("data.txt").await?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).await?;
println!("读取的数据: {:?}", String::from_utf8(buffer));
Ok(())
}
5. tokio::net - 异步网络
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> std::io::Result<()> {
// TCP 服务器
let listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("服务器监听 8080 端口");
loop {
let (socket, addr) = listener.accept().await?;
println!("新连接: {}", addr);
tokio::spawn(async move {
handle_client(socket).await;
});
}
}
async fn handle_client(mut socket: TcpStream) {
let mut buffer = [0; 1024];
loop {
match socket.read(&mut buffer).await {
Ok(0) => break, // 连接关闭
Ok(n) => {
println!("收到 {} 字节", n);
// 回显数据
if socket.write_all(&buffer[..n]).await.is_err() {
break;
}
}
Err(_) => break,
}
}
}
异步并发模式
1. join! - 并发等待多个任务
use tokio;
#[tokio::main]
async fn main() {
// 并发执行,等待所有任务完成
let (res1, res2, res3) = tokio::join!(
async { fetch_data(1).await },
async { fetch_data(2).await },
async { fetch_data(3).await },
);
println!("结果: {}, {}, {}", res1, res2, res3);
}
async fn fetch_data(id: i32) -> String {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
format!("数据 {}", id)
}
2. try_join! - 遇到错误立即返回
use tokio;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 任何一个失败就返回错误
let (res1, res2) = tokio::try_join!(
async { fetch_with_error(1).await },
async { fetch_with_error(2).await },
)?;
println!("成功: {}, {}", res1, res2);
Ok(())
}
async fn fetch_with_error(id: i32) -> Result<String, String> {
if id == 2 {
Err(String::from("错误:ID=2"))
} else {
Ok(format!("数据 {}", id))
}
}
3. select! - 竞争执行
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
tokio::select! {
result = async_task_a() => {
println!("任务 A 先完成: {}", result);
}
result = async_task_b() => {
println!("任务 B 先完成: {}", result);
}
_ = sleep(Duration::from_secs(3)) => {
println!("超时!");
}
}
}
async fn async_task_a() -> String {
sleep(Duration::from_secs(2)).await;
String::from("A")
}
async fn async_task_b() -> String {
sleep(Duration::from_secs(1)).await;
String::from("B")
}
4. spawn - 并发任务(可以独立完成)
use tokio;
#[tokio::main]
async fn main() {
let mut handles = vec![];
// 生成 10 个并发任务
for i in 0..10 {
let handle = tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_millis(i * 100)).await;
println!("任务 {} 完成", i);
i
});
handles.push(handle);
}
// 等待所有任务完成
for handle in handles {
let result = handle.await.unwrap();
println!("结果: {}", result);
}
}
5. JoinSet - 动态任务集合
use tokio::task::JoinSet;
#[tokio::main]
async fn main() {
let mut set = JoinSet::new();
// 动态添加任务
for i in 0..5 {
set.spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_secs(i)).await;
format!("任务 {}", i)
});
}
// 等待任务完成(按完成顺序)
while let Some(result) = set.join_next().await {
match result {
Ok(value) => println!("完成: {}", value),
Err(e) => eprintln!("错误: {}", e),
}
}
}
错误处理与生命周期
1. 异步错误处理
use tokio::fs;
use std::io;
// 返回 Result
async fn read_config() -> io::Result<String> {
let content = fs::read_to_string("config.toml").await?;
Ok(content)
}
// 使用自定义错误类型
#[derive(Debug)]
enum AppError {
IoError(io::Error),
ParseError(String),
}
impl From<io::Error> for AppError {
fn from(err: io::Error) -> Self {
AppError::IoError(err)
}
}
async fn load_config() -> Result<Config, AppError> {
let content = fs::read_to_string("config.toml").await?;
let config = parse_config(&content)
.map_err(|e| AppError::ParseError(e))?;
Ok(config)
}
struct Config;
fn parse_config(s: &str) -> Result<Config, String> {
Ok(Config)
}
2. 生命周期与异步
// async 函数中的引用
async fn process_data(data: &str) -> String {
format!("处理: {}", data)
}
// 返回引用需要明确生命周期
async fn borrow_data<'a>(data: &'a str) -> &'a str {
// 异步操作
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
data
}
// 结构体中的异步方法
struct DataProcessor<'a> {
data: &'a str,
}
impl<'a> DataProcessor<'a> {
async fn process(&self) -> String {
format!("处理: {}", self.data)
}
}
#[tokio::main]
async fn main() {
let data = String::from("测试数据");
let result = process_data(&data).await;
println!("{}", result);
let borrowed = borrow_data(&data).await;
println!("借用: {}", borrowed);
let processor = DataProcessor { data: &data };
let result = processor.process().await;
println!("{}", result);
}
3. 异步闭包(实验性)
use tokio;
#[tokio::main]
async fn main() {
// 普通闭包无法直接异步
// let f = || async { 42 }; // 返回的是 Future,不是值
// 解决方案1:包装在 async 块中
let f = || async { 42 };
let result = f().await;
println!("{}", result);
// 解决方案2:直接使用 async 块
let result = (async { 42 }).await;
println!("{}", result);
// 带参数的情况
let add = |a: i32, b: i32| async move {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
a + b
};
let sum = add(1, 2).await;
println!("和: {}", sum);
}
性能优化
1. 避免不必要的 .await
// ❌ 糟糕:串行执行
async fn bad_example() {
let result1 = fetch_data(1).await;
let result2 = fetch_data(2).await;
let result3 = fetch_data(3).await;
}
// ✅ 良好:并发执行
async fn good_example() {
let (result1, result2, result3) = tokio::join!(
fetch_data(1),
fetch_data(2),
fetch_data(3),
);
}
async fn fetch_data(id: i32) -> String {
format!("数据 {}", id)
}
2. 使用缓冲和批处理
use tokio::io::{AsyncWriteExt, BufWriter};
use tokio::fs::File;
#[tokio::main]
async fn main() -> std::io::Result<()> {
// 使用缓冲写入
let file = File::create("output.txt").await?;
let mut writer = BufWriter::new(file);
for i in 0..1000 {
writer.write_all(format!("行 {}\n", i).as_bytes()).await?;
}
writer.flush().await?;
Ok(())
}
3. 限制并发数(信号量)
use tokio::sync::Semaphore;
use std::sync::Arc;
#[tokio::main]
async fn main() {
let semaphore = Arc::new(Semaphore::new(3)); // 最多3个并发
let mut handles = vec![];
for i in 0..10 {
let permit = semaphore.clone().acquire_owned().await.unwrap();
let handle = tokio::spawn(async move {
println!("任务 {} 开始", i);
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
println!("任务 {} 完成", i);
drop(permit); // 释放许可
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
}
4. 使用 spawn_blocking 处理 CPU 密集型任务
use tokio;
#[tokio::main]
async fn main() {
// CPU 密集型任务应该在独立线程中运行
let result = tokio::task::spawn_blocking(|| {
// 计算密集型操作
let mut sum = 0u64;
for i in 0..1_000_000_000 {
sum += i;
}
sum
}).await.unwrap();
println!("计算结果: {}", result);
}
实战示例
示例 1:异步 HTTP 服务器
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:3000").await?;
println!("HTTP 服务器启动: http://127.0.0.1:3000");
loop {
let (mut socket, addr) = listener.accept().await?;
println!("新连接: {}", addr);
tokio::spawn(async move {
let mut buffer = [0; 1024];
match socket.read(&mut buffer).await {
Ok(n) if n > 0 => {
let request = String::from_utf8_lossy(&buffer[..n]);
println!("收到请求:\n{}", request);
let response = "HTTP/1.1 200 OK\r\n\
Content-Type: text/html; charset=utf-8\r\n\
\r\n\
<html><body>\
<h1>Hello from Async Rust!</h1>\
<p>这是一个异步 HTTP 服务器</p>\
</body></html>";
let _ = socket.write_all(response.as_bytes()).await;
}
_ => {}
}
});
}
}
示例 2:异步数据库查询(本项目实际场景)
use sqlx::MySqlPool;
use tokio;
#[derive(Debug)]
struct User {
id: i64,
username: String,
email: String,
}
async fn get_user_by_id(pool: &MySqlPool, id: i64) -> Result<Option<User>, sqlx::Error> {
let user = sqlx::query_as!(
User,
"SELECT id, username, email FROM users WHERE id = ?",
id
)
.fetch_optional(pool)
.await?;
Ok(user)
}
async fn create_user(pool: &MySqlPool, username: &str, email: &str) -> Result<i64, sqlx::Error> {
let result = sqlx::query!(
"INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)",
username,
email,
"hashed_password"
)
.execute(pool)
.await?;
Ok(result.0 as i64)
}
async fn get_all_users(pool: &MySqlPool) -> Result<Vec<User>, sqlx::Error> {
let users = sqlx::query_as!(
User,
"SELECT id, username, email FROM users"
)
.fetch_all(pool)
.await?;
Ok(users)
}
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = MySqlPool::connect("mysql://root:password@localhost/test").await?;
// 并发创建多个用户
let (id1, id2, id3) = tokio::try_join!(
create_user(&pool, "alice", "alice@example.com"),
create_user(&pool, "bob", "bob@example.com"),
create_user(&pool, "charlie", "charlie@example.com"),
)?;
println!("创建用户 IDs: {}, {}, {}", id1, id2, id3);
// 查询用户
if let Some(user) = get_user_by_id(&pool, id1).await? {
println!("找到用户: {:?}", user);
}
// 获取所有用户
let users = get_all_users(&pool).await?;
println!("所有用户: {:?}", users);
Ok(())
}
示例 3:异步 Redis 缓存
use redis::AsyncCommands;
use tokio;
async fn set_cache(key: &str, value: &str) -> redis::RedisResult<()> {
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_async_connection().await?;
con.set_ex(key, value, 3600).await?; // 1小时过期
Ok(())
}
async fn get_cache(key: &str) -> redis::RedisResult<Option<String>> {
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_async_connection().await?;
let value: Option<String> = con.get(key).await?;
Ok(value)
}
async fn cache_aside_pattern(
pool: &sqlx::MySqlPool,
user_id: i64,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
let cache_key = format!("user:{}", user_id);
// 1. 先查缓存
if let Some(cached) = get_cache(&cache_key).await? {
println!("缓存命中");
return Ok(Some(cached));
}
// 2. 缓存未命中,查数据库
println!("缓存未命中,查询数据库");
let user: Option<User> = sqlx::query_as!(
User,
"SELECT id, username, email FROM users WHERE id = ?",
user_id
)
.fetch_optional(pool)
.await?;
// 3. 写入缓存
if let Some(ref u) = user {
let json = serde_json::to_string(u)?;
set_cache(&cache_key, &json).await?;
}
Ok(user.map(|u| serde_json::to_string(&u).unwrap()))
}
#[derive(Debug, serde::Serialize)]
struct User {
id: i64,
username: String,
email: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pool = sqlx::MySqlPool::connect("mysql://root:password@localhost/test").await?;
// 第一次查询(数据库)
let result = cache_aside_pattern(&pool, 1).await?;
println!("第一次: {:?}", result);
// 第二次查询(缓存)
let result = cache_aside_pattern(&pool, 1).await?;
println!("第二次: {:?}", result);
Ok(())
}
示例 4:异步爬虫(限流 + 重试)
use tokio;
use tokio::time::{sleep, Duration};
use std::sync::Arc;
use tokio::sync::Semaphore;
struct WebCrawler {
client: reqwest::Client,
semaphore: Arc<Semaphore>,
}
impl WebCrawler {
fn new(max_concurrent: usize) -> Self {
Self {
client: reqwest::Client::new(),
semaphore: Arc::new(Semaphore::new(max_concurrent)),
}
}
async fn fetch_with_retry(&self, url: &str, max_retries: u32) -> Result<String, String> {
let _permit = self.semaphore.acquire().await.unwrap();
for attempt in 0..max_retries {
match self.client.get(url).send().await {
Ok(response) => {
if response.status().is_success() {
match response.text().await {
Ok(text) => {
println!("✓ 成功获取: {}", url);
return Ok(text);
}
Err(e) => {
eprintln!("✗ 读取失败 (尝试 {}): {}", attempt + 1, e);
}
}
} else {
eprintln!("✗ HTTP 错误 {}: {}", response.status(), url);
}
}
Err(e) => {
eprintln!("✗ 请求失败 (尝试 {}): {}", attempt + 1, e);
}
}
if attempt < max_retries - 1 {
sleep(Duration::from_secs(2u64.pow(attempt))).await; // 指数退避
}
}
Err(format!("失败: {} (重试 {} 次)", url, max_retries))
}
async fn crawl_urls(&self, urls: Vec<String>) -> Vec<Result<String, String>> {
let mut handles = vec![];
for url in urls {
let crawler = self.clone();
let handle = tokio::spawn(async move {
crawler.fetch_with_retry(&url, 3).await
});
handles.push(handle);
}
let mut results = vec![];
for handle in handles {
results.push(handle.await.unwrap());
}
results
}
}
impl Clone for WebCrawler {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
semaphore: Arc::clone(&self.semaphore),
}
}
}
#[tokio::main]
async fn main() {
let crawler = WebCrawler::new(3); // 最多3个并发
let urls = vec![
"https://httpbin.org/delay/1".to_string(),
"https://httpbin.org/delay/2".to_string(),
"https://httpbin.org/status/500".to_string(), // 会失败
"https://httpbin.org/delay/1".to_string(),
];
let results = crawler.crawl_urls(urls).await;
println!("\n=== 结果 ===");
for (i, result) in results.iter().enumerate() {
match result {
Ok(_) => println!("URL {}: 成功", i),
Err(e) => println!("URL {}: {}", i, e),
}
}
}
示例 5:实时聊天服务器
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::broadcast;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use std::error::Error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
let (tx, _rx) = broadcast::channel(100);
println!("聊天服务器启动: 127.0.0.1:8080");
loop {
let (socket, addr) = listener.accept().await?;
let tx = tx.clone();
let rx = tx.subscribe();
tokio::spawn(async move {
handle_client(socket, addr.to_string(), tx, rx).await;
});
}
}
async fn handle_client(
socket: TcpStream,
addr: String,
tx: broadcast::Sender<String>,
mut rx: broadcast::Receiver<String>,
) {
let (reader, mut writer) = socket.into_split();
let mut reader = BufReader::new(reader);
let mut line = String::new();
// 欢迎消息
let welcome = format!("欢迎 {}! 输入消息开始聊天\n", addr);
let _ = writer.write_all(welcome.as_bytes()).await;
// 广播加入消息
let _ = tx.send(format!("{} 加入聊天室", addr));
loop {
tokio::select! {
// 接收用户输入
result = reader.read_line(&mut line) => {
match result {
Ok(0) => break, // EOF
Ok(_) => {
let msg = format!("{}: {}", addr, line.trim());
let _ = tx.send(msg);
line.clear();
}
Err(_) => break,
}
}
// 接收广播消息
result = rx.recv() => {
match result {
Ok(msg) => {
let response = format!("{}\n", msg);
if writer.write_all(response.as_bytes()).await.is_err() {
break;
}
}
Err(_) => break,
}
}
}
}
println!("{} 断开连接", addr);
}
常见陷阱与解决方案
1. 忘记 .await
// ❌ 错误:忘记 await
async fn wrong() {
let future = async_function(); // 这只是创建了 Future,没有执行
println!("{:?}", future);
}
// ✅ 正确
async fn correct() {
let result = async_function().await; // 真正执行
println!("{}", result);
}
async fn async_function() -> String {
String::from("result")
}
2. 在非异步上下文中使用 await
// ❌ 错误:不能在同步函数中使用 await
fn sync_function() {
// let result = async_function().await; // 编译错误
}
// ✅ 解决方案:使用 block_on
fn sync_function() {
let runtime = tokio::runtime::Runtime::new().unwrap();
let result = runtime.block_on(async {
async_function().await
});
}
async fn async_function() -> String {
String::from("result")
}
3. 阻塞异步运行时
// ❌ 错误:在异步任务中使用阻塞操作
async fn bad_blocking() {
std::thread::sleep(std::time::Duration::from_secs(1)); // 阻塞整个线程
}
// ✅ 正确:使用异步 sleep
async fn good_async() {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
// ✅ 或者使用 spawn_blocking
async fn good_blocking() {
tokio::task::spawn_blocking(|| {
std::thread::sleep(std::time::Duration::from_secs(1));
}).await.unwrap();
}
4. 过度使用 Arc
// ❌ 可能有更好的方案
use std::sync::Arc;
use tokio::sync::Mutex;
async fn suboptimal(data: Arc<Mutex<Vec<i32>>>) {
let mut d = data.lock().await;
d.push(1);
}
// ✅ 考虑使用消息传递
use tokio::sync::mpsc;
async fn better() {
let (tx, mut rx) = mpsc::channel(100);
tokio::spawn(async move {
while let Some(msg) = rx.recv().await {
// 处理消息
}
});
tx.send(1).await.unwrap();
}
总结
核心概念
| 概念 | 说明 | 关键点 |
|---|---|---|
| Future | 异步计算的抽象 | 惰性、需要轮询 |
| async/await | 语法糖 | 简化异步代码编写 |
| 执行器 | 驱动 Future 执行 | Tokio、async-std |
| 任务 | 轻量级并发单元 | 类似绿色线程 |
选择指南
// CPU 密集型
tokio::task::spawn_blocking(|| {
// 计算密集型任务
});
// I/O 密集型
tokio::spawn(async {
// 异步 I/O 操作
});
// 需要精确控制
async {
// 直接使用 async 块
}
本项目应用
// src/main.rs
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Axum HTTP 服务器
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await?;
}
// src/services/user_service.rs
pub async fn create_user(&self, payload: CreateUserRequest) -> Result<User> {
// 异步数据库操作
let user = sqlx::query_as!(User, "INSERT INTO...")
.execute(&self.db)
.await?;
// 异步缓存操作
self.cache.set(&key, &json, 3600).await?;
Ok(user)
}
最佳实践
- ✅ 使用
tokio::join!并发执行独立任务 - ✅ 使用
tokio::select!实现超时和取消 - ✅ 使用
Semaphore限制并发数 - ✅ CPU 密集型任务用
spawn_blocking - ✅ 优先使用消息传递而非共享状态
延伸阅读
- Tokio Tutorial
- Async Book
- Futures Explained
- 本项目异步应用:
src/main.rs,src/services/,src/grpc/