Rust 异步编程

`async`/`await`、`Future`、`tokio`、以及并发与并行的区别。

Advanced
Key Points`async fn` 返回一个 `Future`,**不执行**;必须被 executor 轮询才推进。

基本形态

TOML
[dependencies]
tokio = { version = "1", features = ["full"] }
Rust
async fn fetch_data() -> String {
    println!("开始");
    tokio::time::sleep(std::time::Duration::from_millis(100)).await;
    "数据".to_string()
}

#[tokio::main]
async fn main() {
    let data = fetch_data().await;
    println!("{}", data);
}
Output
开始
数据

#[tokio::main] wraps async fn main in a synchronous main that starts the tokio runtime. Without a runtime, an async function does nothing — it only builds a Future.

并发 vs 并行

Rust
use std::time::Instant;

async fn task(name: &str, ms: u64) -> String {
    tokio::time::sleep(std::time::Duration::from_millis(ms)).await;
    format!("{} 完成", name)
}

#[tokio::main]
async fn main() {
    // 串行:总耗时约 300ms
    let t = Instant::now();
    let a = task("a", 100).await;
    let b = task("b", 100).await;
    let c = task("c", 100).await;
    println!("{} {} {} 串行 {:?}", a, b, c, t.elapsed());

    // 并发:总耗时约 100ms
    let t = Instant::now();
    let (a, b, c) = tokio::join!(
        task("a", 100),
        task("b", 100),
        task("c", 100)
    );
    println!("{} {} {} 并发 {:?}", a, b, c, t.elapsed());
}
Output
a 完成 b 完成 c 完成 串行 301ms
a 完成 b 完成 c 完成 并发 101ms

join! drives three futures concurrently (interleaved on one thread); spawn adds multi-threaded parallelism. That is the difference: async is waiting without blocking, not using more cores.

spawn 并行

Rust
#[tokio::main]
async fn main() {
    let h1 = tokio::spawn(async { 1 + 1 });
    let h2 = tokio::spawn(async { 2 + 2 });

    let (a, b) = (h1.await.unwrap(), h2.await.unwrap());
    println!("{} {}", a, b);
}
Output
2 4
tokio::spawn requires a 'static + Send future, so borrowing locals is not allowed — move or clone owned values in. That is why async code clones more.

Future 是什么

Rust
// Future 就是一个可以被反复轮询的状态机
trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

enum Poll<T> {
    Ready(T),
    Pending,
}

async fn is compiled into a state machine implementing Future: each .await is a suspension point. Pending means "not ready, yield the thread"; Ready means done.

常见组合子

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

#[tokio::main]
async fn main() {
    // join!:等全部完成
    let (a, b) = tokio::join!(sleep(Duration::from_millis(10)), async { 42 });
    let _ = a;
    println!("{}", b);

    // try_join!:任一失败就返回
    // select!:谁先完成用谁
    let mut t1 = Box::pin(sleep(Duration::from_millis(50)));
    let mut t2 = Box::pin(sleep(Duration::from_millis(20)));
    tokio::select! {
        _ = &mut t1 => println!("t1 先"),
        _ = &mut t2 => println!("t2 先"),
    }
}
Output
42
t2 先
MacroBehaviour
join!等全部完成
try_join!等全部完成,遇错即返回
select!第一个完成的胜出,其余取消
spawn丢到运行时并行跑

超时与取消

Rust
use tokio::time::{timeout, Duration};

async fn slow() -> &'static str {
    tokio::time::sleep(Duration::from_secs(10)).await;
    "完成"
}

#[tokio::main]
async fn main() {
    match timeout(Duration::from_millis(100), slow()).await {
        Ok(v) => println!("{}", v),
        Err(_) => println!("超时了"),
    }
}
Output
超时了

A timeout cancels the future by dropping it. This is where Rust async shines: cancellation needs no API — dropping is cancellation.

Never call blocking functions in async code (std::fs::read, std::thread::sleep, CPU-heavy work) — they stall the whole executor thread. Use tokio::fs, tokio::time::sleep, and spawn_blocking for CPU work.

Friends & Partners