关键概念对比
功能 | 多线程方式 | 异步方式 |
---|---|---|
创建并发单元 | thread::spawn() |
trpl::spawn_task() |
延时等待 | thread::sleep() (阻塞线程) |
trpl::sleep().await (非阻塞) |
等待结束 | join() (阻塞线程) |
.await (让出控制权) |
并发单位 | 操作系统线程 (重量级) | Future (轻量级状态机) |
三种异步模式详解
1️⃣ 基础任务生成(示例17-6)
rust
# extern crate trpl; // required for mdbook test
# use std::time::Duration;
fn main() {
trpl::run(async {
trpl::spawn_task(async {
for i in 1..10 {
println!("hi number {i} from the first task!");
trpl::sleep(Duration::from_millis(500)).await;
}
});
for i in 1..5 {
println!("hi number {i} from the second task!");
trpl::sleep(Duration::from_millis(500)).await;
}
});
}
特点:
- 主任务结束时自动终止子任务
- 类似守护线程(daemon thread)
- 输出顺序随机(取决于运行时调度)
2️⃣ 使用任务句柄等待
rust
# extern crate trpl; // required for mdbook test
#
# use std::time::Duration;
#
# fn main() {
# trpl::run(async {
let handle = trpl::spawn_task(async {
for i in 1..10 {
println!("hi number {i} from the first task!");
trpl::sleep(Duration::from_millis(500)).await;
}
});
for i in 1..5 {
println!("hi number {i} from the second task!");
trpl::sleep(Duration::from_millis(500)).await;
}
handle.await.unwrap();
# });
#}
核心改进:
- 通过
handle.await
确保子任务完整执行 - 类似线程的
join()
,但非阻塞 - 输出顺序依然随机
3️⃣ 使用join组合Future(示例17-8)
rust
# extern crate trpl; // required for mdbook test
#
# use std::time::Duration;
#
# fn main() {
# trpl::run(async {
let fut1 = async {
for i in 1..10 {
println!("hi number {i} from the first task!");
trpl::sleep(Duration::from_millis(500)).await;
}
};
let fut2 = async {
for i in 1..5 {
println!("hi number {i} from the second task!");
trpl::sleep(Duration::from_millis(500)).await;
}
};
trpl::join(fut1, fut2).await;
# });
#}
革命性变化:
- 不需要生成任务,直接操作 Future 对象
- 运行时公平轮询(round-robin)
- 输出顺序固定(交替执行)
text
hi number 1 from the first task! // fut1
hi number 1 from the second task! // fut2
hi number 2 from the first task! // fut1
...
为什么顺序固定?关键机制
-
公平调度器 :
trpl::join
内部以固定频率轮询每个 Future- 每次只推进少量执行(到下一个
.await
点)
-
协作式让步 :
rust// 每次遇到 .await 都会让出控制权 trpl::sleep(...).await; // ↑ 让出点
-
执行流程 :
- 轮询 fut1 → 执行到第一个
sleep().await
→ 暂停 - 轮询 fut2 → 执行到第一个
sleep().await
→ 暂停 - 重复直到两者完成
- 轮询 fut1 → 执行到第一个
与线程的本质区别
特性 | 线程模型 | 异步模型 |
---|---|---|
内存开销 | MB级(线程栈) | KB级(状态机) |
切换代价 | 高(内核切换) | 极低(用户态切换) |
调度单位 | 整个线程 | 单个 .await 分段 |
控制粒度 | 粗(操作系统控制) | 细(程序员可控) |
实践建议
-
何时用异步:
- I/O密集型任务(网络请求/文件操作)
- 高并发服务(如Web服务器)
- 需要数千以上"并发单元"的场景
-
何时用线程:
- CPU密集型计算
- 阻塞型操作(如复杂计算)
- 需要利用多核并行(非并发)
-
混合使用:
rust// CPU密集型任务放在专用线程 tokio::task::spawn_blocking(|| heavy_computation());
真实世界映射
示例中的伪API | 实际生产级对应 |
---|---|
trpl::run |
#[tokio::main] |
trpl::sleep |
tokio::time::sleep() |
trpl::join |
tokio::join! 宏 |
spawn_task |
tokio::spawn() |