Skip to content

快速开始 ​

并发编程初体验 ​

  1. 同时请求两个网页, 提取网页的 title
  2. 哪个网页先提取完成就先输出哪个网页的 title
  3. 实现异步发送请求并解析提取html文档的 tilte
rust
use trpl::{self, Either, Html};

fn main() {
    let urls: Vec<String> = vec![
        String::from("https://rust-lang.org/"),
        String::from("https://www.baidu.com/"),
    ];

    // 绑定异步运行时, 所有异步代码都用这个 trpl::run 函数来执行
    trpl::run(async {
        let fut1 = get_page_title(&urls[0]);
        let fut2 = get_page_title(&urls[0]);

        // trpl::race: 对比两个 future 哪个先完成就会匹配到哪个
        let title = match trpl::race(fut1, fut2).await {
            Either::Left(title) => title,
            Either::Right(title) => title,
        };

        // 可能是百度, 也可能是 rust-lang.org
        println!("title: {}", title.unwrap());
    });
}

// async/await 语法
async fn get_page_title(url: &str) -> Option<String> {
    let response = trpl::get(url).await;
    let body = response.text().await;
    Html::parse(&body)
        .select_first("title")
        .map(|title_el| title_el.inner_html().trim().to_string())
}
rust
// 手动返回 Future 实现
//  async/await 语法只是语法糖, 最终编译器还是会将代码变为这个样子
fn get_page_title(url: &str) -> impl std::future::Future<Output = Option<String>> + '_ {
    async move {
        let response = trpl::get(url).await;
        let body = response.text().await;
        Html::parse(&body)
            .select_first("title")
            .map(|title_el| title_el.inner_html().trim().to_string())
    }
}

顺序执行 vs 并发执行 ​

rust
use std::time::Instant;
use trpl::{self, Html};

// 异步发送请求并解析提取html文档的title
async fn get_page_title(url: &str) -> Option<String> {
    let response = trpl::get(url).await;
    let body = response.text().await;
    Html::parse(&body)
        .select_first("title")
        .map(|title_el| title_el.inner_html().trim().to_string())
}

fn main() {
    let urls: Vec<String> = vec![
        String::from("https://rust-lang.org/"),
        String::from("https://play.rust-lang.org"),
        String::from("https://doc.rust-lang.org/std/index.html"),
        String::from("https://doc.rust-lang.org/book"),
        String::from("https://doc.rust-lang.org/cargo/index.html"),
        String::from("https://doc.rust-lang.org/rustdoc/index.html"),
        String::from("https://doc.rust-lang.org/rustc/index.html"),
        String::from("https://rust-cli.github.io/book/index.html"),
        String::from("https://doc.rust-lang.org/error_codes/error-index.html"),
    ];

    // 顺序执行
    trpl::run(async {
        println!("=== 顺序执行 ===");
        let start = Instant::now();

        for url in urls.iter() {
            let title = get_page_title(url).await;
            println!("title: {}", title.unwrap());
        }

        println!("顺序执行总耗时: {:?}", start.elapsed());
    });

    // 并发执行
    trpl::run(async {
        println!("=== 并发执行 ===");
        let start = Instant::now();
        let futures: Vec<_> = urls.iter().map(|url| get_page_title(url)).collect();
        let titles = trpl::join_all(futures).await;

        for title in titles {
            println!("title: {}", title.unwrap());
        }

        println!("并发执行总耗时: {:?}", start.elapsed());
    });
}

性能影响关键 ​

  • 顺序执行: 请求1 → 等待完成 → 请求2 → 等待完成 -> ...(总耗时 = 9个请求耗时之和)
  • 并发执行: 请求1-9 同时发起(总耗时 ≈ 最慢的那个请求的耗时)

[注意]

  1. 使用 trpl::join 或 trpl::join_all 才能让异步代码真正并发执行
  2. 否则即使写了 async/await, 如果只是按顺序 .await 仍然是顺序执行的

trpl 介绍 ​

trpl 是 rust 的官方文档, 它是一个全面的 rust 学习教程, 每个章节都有对应的代码, 可以在线运行, 也可以下载到本地运行

Released under the MIT License.