异步与网络

第23课:Tokio运行时

🎯 本课目标

掌握Tokio异步运行时的核心概念:任务、调度器、IO驱动和定时器。

1. Tokio基础

// Cargo.toml
[dependencies]
tokio = { version = "1", features = ["full"] }

// 最简Tokio程序
#[tokio::main]
async fn main() {
    println!("Hello from Tokio!");
}

// 等价于
fn main() {
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async {
            println!("Hello from Tokio!");
        })
}

✅ 编译验证通过

2. 任务与spawn

use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
    // spawn创建并发任务
    let handle = tokio::spawn(async {
        sleep(Duration::from_millis(100)).await;
        "任务完成"
    });

    // 等待任务
    let result = handle.await.unwrap();
    println!("结果: {}", result);

    // 多任务并发
    let mut handles = vec![];
    for i in 0..5 {
        handles.push(tokio::spawn(async move {
            sleep(Duration::from_millis(50 * i as u64)).await;
            i * i
        }));
    }

    for h in handles {
        println!("结果: {}", h.await.unwrap());
    }
}

✅ 编译验证通过

3. 运行时配置

// 多线程运行时(默认)
#[tokio::main]
async fn main() { }

// 单线程运行时(轻量级)
#[tokio::main(flavor = "current_thread")]
async fn main() { }

// 自定义工作线程数
#[tokio::main(worker_threads = 4)]
async fn main() { }

// 手动构建
let rt = tokio::runtime::Builder::new_multi_thread()
    .worker_threads(4)
    .thread_stack_size(2 * 1024 * 1024)
    .enable_all()
    .build()
    .unwrap();

4. Tokio组件

组件功能
tokio::net异步TCP/UDP/Unix
tokio::fs异步文件IO
tokio::time定时器和延时
tokio::sync异步同步原语
tokio::ioAsyncRead/AsyncWrite
tokio::signal信号处理

5. 异步同步原语

use tokio::sync::{Mutex, Semaphore, broadcast, mpsc};

#[tokio::main]
async fn main() {
    // 异步Mutex(跨.await持有)
    let data = Mutex::new(0);
    {
        let mut lock = data.lock().await;
        *lock += 1;
    }

    // 信号量(限制并发数)
    let sem = Semaphore::new(3);  // 最多3个并发
    let permit = sem.acquire().await.unwrap();

    // 广播通道
    let (tx, mut rx1) = broadcast::channel(16);
    let mut rx2 = tx.subscribe();

    // mpsc通道
    let (tx, mut rx) = mpsc::channel(32);
}
Tokio运行时架构: ┌─────────────────────────────────┐ │ Tokio Runtime │ │ ┌─────────────────────────────┐│ │ │ Work-Stealing Scheduler ││ │ │ ┌──────┐ ┌──────┐ ┌──────┐││ │ │ │Worker│ │Worker│ │Worker│││ ← 操作系统线程 │ │ │Thread│ │Thread│ │Thread│││ │ │ │┌────┐│ │┌────┐│ │┌────┐││ │ │ ││Task││ ││Task││ ││Task│││ ← 轻量级协程 │ │ ││┌──┐││ ││┌──┐││ ││┌──┐││ │ │ │││Fut│││ │││Fut│││ │││Fut││ ← Future状态机 │ │ ││└──┘││ ││└──┘││ ││└──┘││ │ │ │└────┘│ │└────┘│ │└────┘││ │ │ └──────┘ └──────┘ └──────┘││ │ └─────────────────────────────┘│ │ ┌─────────────────────────────┐│ │ │ IO Driver (epoll) ││ │ └─────────────────────────────┘│ │ ┌─────────────────────────────┐│ │ │ Timer Wheel ││ │ └─────────────────────────────┘│ └─────────────────────────────────┘

📝 练习题

练习1:速率限制

用Semaphore实现API调用速率限制。

练习2:优雅关闭

实现Ctrl+C信号处理和优雅关闭。

练习3:生产者-消费者

用tokio::sync::mpsc实现异步生产者消费者。

🏆

成就解锁:运行时主人

掌握Tokio——Rust异步生态的核心

🔬 深度理解:异步编程模型

Rust的异步模型与其他语言有本质区别:

1. 编译器生成状态机

async函数不是语法糖生成闭包或Future对象,而是编译器生成一个状态机enum,每个.await点是一个状态。这比Green Thread更高效——无堆分配、无运行时开销。

2. 零成本异步

不使用的异步特性不产生代码。不调用async函数?没有状态机代码。这与其他语言的全局运行时(Go的goroutine调度器、Java的Fiber)不同。

3. 显式运行时

Rust没有内置异步运行时。你选择Tokio、async-std或其他——运行时是库,不是语言特性。这意味着灵活但需要选择。

📊 异步模型对比

模型语言开销并发数
OS线程C/Java1-8MB/线程~1000
Green ThreadGo~2KB/goroutine~100K
async/awaitRust~几百字节~1M+
回调Node.js

🌐 网络编程最佳实践


📚 扩展阅读

以下资源帮助你深入学习本课主题:

🔑 关键术语回顾

本课涉及的核心概念,确保你理解每一个:

术语说明
所有权Rust内存管理的核心机制,每个值有唯一所有者
借用通过引用访问数据,不获取所有权
生命周期引用有效的范围,编译期分析工具
traitRust的接口/抽象机制,类似其他语言的接口
泛型参数化类型,零成本抽象
模式匹配强大的数据解构和分支机制
零成本抽象高层抽象不引入运行时开销
fearless concurrency编译器保证线程安全

💬 学习建议

  1. 动手编码 — 每个代码示例都在本地运行一遍
  2. 修改实验 — 故意改错代码,看编译器报什么错
  3. 完成练习 — 每课的练习题是巩固知识的关键
  4. 阅读源码 — 看标准库和优秀开源项目的实现
  5. 写项目 — 真正掌握需要构建真实项目

🤔 常见问题

Q: Rust学习曲线真的很陡吗?

A: 前期确实需要适应所有权和借用检查器,但一旦理解了,这些概念会让你的代码更可靠。大多数人2-4周就能上手。

Q: Rust适合什么项目?

A: 系统编程、Web服务、CLI工具、嵌入式、WASM、网络服务、数据库等。基本上需要性能和安全的地方都适合。

Q: 遇到编译错误怎么办?

A: Rust编译器的错误信息非常友好!仔细阅读,通常会指出问题所在和修复建议。也可以用cargo clippy获取更多提示。