Skip to content

async.rs

文件信息

  • 📄 原文件:03_async.rs
  • 🔤 语言:rust

完整代码

rust
// ============================================================
//                      异步编程(Async/Await)
// ============================================================
// Rust 的异步编程基于 Future trait
// async fn 返回一个实现了 Future 的值
// await 等待 Future 完成
//
// 【重要】Rust 标准库只提供 Future trait,不提供运行时
// 需要第三方运行时:tokio(最流行)、async-std、smol
//
// 【注意】本文件演示核心概念,实际运行需要 tokio 等运行时
// 添加依赖: cargo add tokio --features full

// 模拟异步运行时的简单实现(用于演示)
// 实际项目中使用 #[tokio::main] 或 #[async_std::main]

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;

fn main() {
    println!("=== 异步编程 ===");

    // ----------------------------------------------------------
    // 1. 基本概念
    // ----------------------------------------------------------
    // async fn 定义异步函数,返回 impl Future<Output = T>
    // .await 暂停当前任务,等待 Future 完成
    //
    // 【与线程的区别】
    // 线程:由操作系统调度,每个线程有独立的栈(MB 级别)
    // 异步:由运行时调度,任务非常轻量(KB 级别)
    //
    // 【适用场景】
    // 线程:CPU 密集型任务
    // 异步:IO 密集型任务(网络请求、文件读写、数据库查询)

    println!("注意: 以下代码展示语法和概念");
    println!("实际运行需要 tokio 等异步运行时\n");

    // ----------------------------------------------------------
    // 2. async fn 语法
    // ----------------------------------------------------------
    // async fn 实际上是语法糖:
    // async fn foo() -> i32 { 42 }
    // 等价于:
    // fn foo() -> impl Future<Output = i32> { async { 42 } }

    println!("=== async fn 语法演示 ===");
    println!("async fn hello() -> String");
    println!("  等价于 fn hello() -> impl Future<Output = String>");

    // ----------------------------------------------------------
    // 3. Future trait
    // ----------------------------------------------------------
    // trait Future {
    //     type Output;
    //     fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
    // }
    //
    // Poll::Ready(value) — Future 已完成,返回值
    // Poll::Pending      — Future 未完成,稍后再试
    //
    // 【重要】Future 是惰性的,不 poll 就不会执行
    // 这与 JavaScript 的 Promise 不同(Promise 创建即开始执行)

    // 手动实现一个简单的 Future
    let countdown = Countdown { remaining: 3 };
    println!("创建了一个 Countdown Future(需要运行时来驱动)");
    println!("Countdown 类型: {:?}", countdown);

    // ----------------------------------------------------------
    // 4. 实际 tokio 代码示例(伪代码展示)
    // ----------------------------------------------------------
    println!("\n=== Tokio 代码示例 ===");

    // 以下是在 tokio 运行时中的实际写法:
    println!(r#"
// Cargo.toml:
// [dependencies]
// tokio = {{ version = "1", features = ["full"] }}
// reqwest = {{ version = "0.11" }}

#[tokio::main]
async fn main() {{
    // 基本异步函数调用
    let result = fetch_data("https://api.example.com").await;
    println!("结果: {{}}", result);

    // 并发执行多个任务
    let (r1, r2, r3) = tokio::join!(
        fetch_data("url1"),
        fetch_data("url2"),
        fetch_data("url3"),
    );

    // 竞争:返回最先完成的
    let fastest = tokio::select! {{
        val = fetch_data("fast_url") => val,
        val = fetch_data("slow_url") => val,
    }};

    // 生成并发任务
    let handle = tokio::spawn(async {{
        // 在独立任务中运行
        heavy_computation().await
    }});
    let result = handle.await.unwrap();
}}

async fn fetch_data(url: &str) -> String {{
    // 模拟网络请求
    tokio::time::sleep(Duration::from_millis(100)).await;
    format!("来自 {{}} 的数据", url)
}}
"#);

    // ----------------------------------------------------------
    // 5. 异步模式
    // ----------------------------------------------------------
    println!("=== 常用异步模式 ===\n");

    // 模式1: join!(并发执行所有任务)
    println!("模式1: tokio::join!(task1, task2, task3)");
    println!("  所有任务并发执行,全部完成后返回\n");

    // 模式2: select!(竞争,取最快的)
    println!("模式2: tokio::select! {{ val = task1 => ..., val = task2 => ... }}");
    println!("  返回最先完成的任务结果\n");

    // 模式3: spawn(后台任务)
    println!("模式3: tokio::spawn(async {{ ... }})");
    println!("  在后台运行,返回 JoinHandle\n");

    // 模式4: Stream(异步迭代器)
    println!("模式4: while let Some(item) = stream.next().await {{ ... }}");
    println!("  异步遍历数据流\n");

    // 模式5: 超时
    println!("模式5: tokio::time::timeout(Duration, future).await");
    println!("  给异步操作设置超时\n");

    // ----------------------------------------------------------
    // 6. 异步错误处理
    // ----------------------------------------------------------
    println!("=== 异步错误处理 ===\n");

    println!(r#"
async fn process() -> Result<String, Box<dyn std::error::Error>> {{
    // ? 操作符在 async 中正常工作
    let data = fetch("url").await?;
    let parsed = parse(&data)?;
    Ok(parsed)
}}

// 重试模式
async fn fetch_with_retry(url: &str, max_retries: u32) -> Result<String, String> {{
    for attempt in 1..=max_retries {{
        match fetch(url).await {{
            Ok(data) => return Ok(data),
            Err(e) if attempt < max_retries => {{
                println!("尝试 {{}} 失败: {{}}, 重试...", attempt, e);
                tokio::time::sleep(Duration::from_secs(attempt as u64)).await;
            }}
            Err(e) => return Err(e),
        }}
    }}
    unreachable!()
}}
"#);

    // ----------------------------------------------------------
    // 7. Send + 'static 约束
    // ----------------------------------------------------------
    println!("=== Send + 'static ===\n");

    println!("tokio::spawn 要求 Future 是 Send + 'static");
    println!("这意味着:");
    println!("  - Future 中引用的数据必须是 Send 的(可跨线程)");
    println!("  - Future 不能包含非 'static 的引用");
    println!("");
    println!("常见错误:");
    println!("  - 在 .await 点持有 MutexGuard(不是 Send 的)");
    println!("  - 在 async 块中使用局部引用");
    println!("");
    println!("解决方法:");
    println!("  - 使用 tokio::sync::Mutex 代替 std::sync::Mutex");
    println!("  - 在 .await 之前释放锁");
    println!("  - 使用 Arc 共享数据");

    // ----------------------------------------------------------
    // 8. 同步 vs 异步的选择
    // ----------------------------------------------------------
    println!("\n=== 何时使用异步 ===\n");

    println!("✅ 使用异步:");
    println!("  - 大量并发 IO(Web 服务器、爬虫、微服务)");
    println!("  - 需要处理数千个连接");
    println!("  - 长时间等待的 IO 操作");
    println!("");
    println!("❌ 不使用异步:");
    println!("  - CPU 密集型计算(用线程 + rayon)");
    println!("  - 简单脚本和 CLI 工具");
    println!("  - 并发量很低的应用");

    println!("\n=== 异步编程结束 ===");
}

// ----------------------------------------------------------
// 手动实现 Future(了解底层原理)
// ----------------------------------------------------------
#[derive(Debug)]
struct Countdown {
    remaining: u32,
}

impl Future for Countdown {
    type Output = String;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if self.remaining == 0 {
            Poll::Ready("倒计时完成!".to_string())
        } else {
            println!("Countdown: 还剩 {}", self.remaining);
            self.remaining -= 1;
            cx.waker().wake_by_ref();  // 通知运行时再次 poll
            Poll::Pending
        }
    }
}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布