第八篇: Rust Async/Await 异步编程完全指南

54 阅读15分钟

Rust Async/Await 异步编程完全指南

Rust 的异步编程基于 Future trait 和 async/await 语法,提供零成本抽象的高性能异步 I/O。

目录

  1. 异步编程基础
  2. Future 和执行器
  3. async/await 语法
  4. Tokio 异步运行时
  5. 异步并发模式
  6. 错误处理与生命周期
  7. 性能优化
  8. 实战示例

异步编程基础

为什么需要异步编程?

同步 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)
}

最佳实践

  1. ✅ 使用 tokio::join! 并发执行独立任务
  2. ✅ 使用 tokio::select! 实现超时和取消
  3. ✅ 使用 Semaphore 限制并发数
  4. ✅ CPU 密集型任务用 spawn_blocking
  5. ✅ 优先使用消息传递而非共享状态

延伸阅读