并发任务
你会学到什么
tokio::join!让多个 Future 并发推进,总耗时约等于最慢的那个。tokio::spawn把任务交给运行时调度,返回JoinHandle。- 并发(concurrency)和并行(parallelism)的区别。
最小示例
let (a, b) = tokio::join!(fetch(1), fetch(2)); // 两个同时进行
运行代码
cd examples
cargo run -p rt_33_async_tasks
cargo test -p rt_33_async_tasks
代码讲解
顺序 .await 会一个等完再等下一个;join! 则同时驱动所有 Future:
async fn run_concurrently() -> Vec<u32> {
let (first, second, third) = tokio::join!(work(1, 30), work(2, 10), work(3, 20));
vec![first, second, third]
}
join! 按参数顺序返回结果,和谁先完成无关。
spawn 适合“发射后不管”或需要 JoinHandle 的场景:
let handle = tokio::spawn(work(1, 5));
let value = handle.await.unwrap();
注意 spawn 的任务必须 'static 且 Send,因为它可能在另一个线程上运行。
常见错误
误以为 join! 会按完成顺序返回:
let (a, b) = tokio::join!(slow(), fast());
// a 仍然是 slow() 的结果,顺序由参数位置决定
练习
- 用
join!并发请求 3 个 id,对比顺序 await 的总耗时。 - 用
spawn启动 5 个任务,收集所有JoinHandle的结果求和。
小结
join! 并发等待一组 Future,spawn 把任务交给调度器。异步并发用单线程也能重叠等待时间。
下一步
接下来进入生态库阶段,学习 serde、anyhow 和 clap。
完整示例代码
下面是 examples/33_async_tasks/ 的完整源码。无需 clone 仓库,直接在页面上阅读、复制、对照运行。
examples/33_async_tasks/src/main.rs
//! 并发任务:join! 同时等待,spawn 后台执行。
use std::time::Duration;
async fn work(id: u32, delay_ms: u64) -> u32 {
tokio::time::sleep(Duration::from_millis(delay_ms)).await;
id * 10
}
/// join! 让多个 Future 并发推进,而不是顺序等待。
async fn run_concurrently() -> Vec<u32> {
let (first, second, third) = tokio::join!(work(1, 30), work(2, 10), work(3, 20));
vec![first, second, third]
}
/// spawn 把任务交给运行时调度,返回 JoinHandle。
async fn spawn_sum() -> u32 {
let mut handles = Vec::new();
for id in 1..=3 {
handles.push(tokio::spawn(work(id, 5)));
}
let mut total = 0;
for handle in handles {
total += handle.await.expect("任务 panic");
}
total
}
#[tokio::main]
async fn main() {
println!("concurrent = {:?}", run_concurrently().await);
println!("spawned sum = {}", spawn_sum().await);
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn joins_in_order() {
// join! 按参数顺序返回结果,与完成先后无关。
assert_eq!(run_concurrently().await, vec![10, 20, 30]);
}
#[tokio::test]
async fn spawns_and_sums() {
assert_eq!(spawn_sum().await, 60);
}
} examples/33_async_tasks/Cargo.toml
[package]
name = "rt_33_async_tasks"
version.workspace = true
edition.workspace = true
publish.workspace = true
[dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }