4. tokio::spawn 并发执行独立任务
tokio::spawn 的任务模型
tokio::spawn 接收一个 Future(通常写成 async 块),把它交给运行时,返回一个 JoinHandle<T>。任务一旦被派生,就与调用者解耦:调用者继续往下走,任务在运行时的调度器上被并发推进。要拿到任务的返回值,需要 .await 对应的 JoinHandle;如果任务 panic,.await 会得到 Err(JoinError)。
关键约束:tokio::spawn 要求 Future 满足 Send + 'static。这意味着任务不能借用栈上的局部变量,跨任务共享数据要用 Arc、clone 或 move 把所有权搬进任务。
完整可运行示例
下面示例派生三个独立任务,每个任务睡眠不同时长后返回一个整数,主任务按派生顺序 .await 收集结果。注意 move 把 id 和 ms 的所有权搬进任务,满足 'static 约束。
use std::time::Instant;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let start = Instant::now();
// 派生三个独立任务,返回 JoinHandle<i32>
let h1 = tokio::spawn(async move {
sleep(Duration::from_millis(300)).await;
println!("[A] 完成 @ {:?}", start.elapsed());
1
});
let h2 = tokio::spawn(async move {
sleep(Duration::from_millis(150)).await;
println!("[B] 完成 @ {:?}", start.elapsed());
2
});
let h3 = tokio::spawn(async move {
sleep(Duration::from_millis(100)).await;
println!("[C] 完成 @ {:?}", start.elapsed());
3
});
// 按派生顺序收集结果
let r1 = h1.await.expect("task A panicked");
let r2 = h2.await.expect("task B panicked");
let r3 = h3.await.expect("task C panicked");
println!("结果: {} {} {}", r1, r2, r3);
println!("总耗时: {:?}", start.elapsed());
}
预期输出(时间戳为示例,实际略有浮动):
[C] 完成 @ 100.xxms
[B] 完成 @ 150.xxms
[A] 完成 @ 300.xxms
结果: 1 2 3
总耗时: 300.xxms
完成顺序是 C→B→A,但收集顺序仍是 1 2 3,因为
.await 按代码书写顺序等待。总耗时约等于最慢任务(300ms),而不是三个任务之和(550ms),这正是并发调度的直接证据。观察并发调度的三个要点
- 完成顺序 ≠ 派生顺序:短任务先打印,说明运行时在并发推进多个 Future,而不是串行执行。
- 总耗时取最大值:若把三个
sleep直接写在主任务里顺序.await,总耗时会是 550ms。用spawn后降到约 300ms。 - JoinHandle 是结果通道:不
.await就丢弃 handle,任务仍会运行,但拿不到返回值,也无法感知 panic。
常见错误与修正
| 错误写法 | 编译/运行表现 | 修正 |
|---|---|---|
tokio::spawn(async { println!("{}", local); }) | 编译报错:借用数据逃逸,不满足 'static | 加 move,或把 local 克隆进任务 |
在任务里用 std::thread::sleep | 阻塞工作线程,其他任务被拖慢 | 改用 tokio::time::sleep(...).await |
| 派生后不保存 handle | 任务照常跑,但结果和 panic 被静默丢弃 | 保留 JoinHandle 并 .await |
在 spawn 的任务里 .await 另一个未 spawn 的 Future | 该 Future 仍在该任务内串行执行 | 需要真正并发就再 spawn 一次 |
若某个任务 panic,
handle.await 返回 Err(JoinError),expect 会让主任务也 panic。生产代码中应显式处理 JoinError,例如记录日志后继续收集其他任务结果。