rust学习十七.1、async和await

本文对应相关书籍17.1章节。

先言:async和多线程没有什么密切关系,当然也不是毫无关系!

一、async在rust到底是什么

async中文的意思是异步,在编程中指的是:执行一段代码,主线程可以在等待完成之前,先做其它事情。

如果有写过前端代码,就明白这个东西。例如经典的jquery的ajax默认就是异步执行的。

 

原书花费了一段文字先解释:并行和并发

 * 1.并行(parallel)和并发(concurrency)都有一段时间内共同执行多个任务的意思
 * 2.并行指的是在同一时刻,有多个任务在执行。并发是指在某一时间段内,有多个任务在交替执行
 * 3.并发某种程度上理解为资源不够,分时处理;并行理解为资源充足,同时处理。
 * 4.当使用 Rust 中的 async 时,我们总是在处理并发
 * 5.取决于硬件、操作系统和所使用的异步运行时(async runtime)-- 稍后会介绍更多的异步运行时!
 *   并发也可能在底层使用了并行 --  这个需要特别注意

说了这么一大段就是为了说明一个事情:使用 Rust 中的 async 时,我们总是在处理并发

也就是说,一般情况下,在rust中使用异步,基本就是意味着是cpu分时处理,但具体怎么实现和代码以及系统系统有关!!!

 

二、async、await和future

await-等待

future-未来,rust中用于表示一个数据类型:现在不会有,但是过了一段时间会有

这些都是很熟悉的字眼,在java,js中是常常出现的。

 * 1.future 是一个现在可能还没有准备好但将在未来某个时刻准备好的值
 * 2.Rust 提供了 Future trait 作为基础组件,这样不同的异步操作就可以在不同的数据结构上实现
 * 3.每一个实现了 Future 的类型会维护自己的进度状态信息和 “ready” 的定义
 * 4.async 关键字可以用于代码块和函数
 * 5.在一个 async 块或 async 函数中,可以使用 await 关键字来等待一个 future 准备就绪,这一过程称为 等待一个 future
 * 6.检查一个 future 并查看其值是否已经准备就绪的过程被称为 轮询(polling)
 * 7.在大多数情况下,编写异步 Rust 代码时,我们使用 async 和 await 关键字。
 *    Rust 将其编译为等同于使用 Future trait 的代码,这非常类似于将 for 循环编译为等同于使用 Iterator trait 的代

 

三、示例

复制代码
use trpl::{Either, Html};
use std::thread;

fn main() {
    let args: Vec<String> = std::env::args().collect();
    println!("{:?}", args);

    //注意async关键字,并不会导致开启一个新的线程,仅仅意味着可以在内部使用await关键字等待异步操作完成。
    trpl::run(async {
        //lazy future 这里不会执行
        let title_fut_1 = page_title(&args[1]);
        //lazy future 这里不会执行
        let title_fut_2 = page_title(&args[2]);

        //race中的代码使用future::select执行并发,并返回最先完成的future。
        //因为这里真正开始执行,所以才会在多次运行的时候,返回可能不同的结果
        //race方法返回的是一个Either类型,Either是一个枚举类型,有两个值,Left和Right。
        let (url, maybe_title) = match trpl::race(title_fut_1, title_fut_2).await {
            Either::Left(left) => left,
            Either::Right(right) => right,
        };

        println!("{url} returned first");
        match maybe_title {
            Some(title) => println!("Its page title is: '{title}'"),
            None => println!("Its title could not be parsed."),
        }
    })
}
/**
 * 带了async的函数会返回一个future,这个future的类型是impl Future<Output = ()>,
 * output是一个泛型参数,这里是(),表示这个future的返回值是一个元组,
 */
async fn page_title(url: &str) -> (&str, Option<String>) {
    //这个语句会证实函数page_title是运行在主线程中...,并不是多线程的.
    println!("{}",url);
    println!("线程{:?}正在执行", thread::current().id());
    let text = trpl::get(url).await.text().await;
    let title = Html::parse(&text)
        .select_first("title")
        .map(|title| title.inner_html());
    (url, title)
}
复制代码

 

注意:第一次执行,你会怀疑是不是卡死了,这个不是问题,是rusts-script需要耗费时间处理trpl(后者又依赖很多乱起八糟的),所以耗时颇长.

rust-script -d trpl 17.2_future.rs  https://www.cnblogs.com/lzfhope/p/18459068  https://www.cnblogs.com/lzfhope/p/18664452

这个命令执行了几次:

可以看到,结果会有所不同,这意味着 两个page_title在执行的时候不是固定,不是串行的!

 

这个trpl是rust提供的示例代码,下面是感兴趣的部分:

复制代码
pub fn run<F: Future>(future: F) -> F::Output {
    let rt = Runtime::new().unwrap();
    rt.block_on(future)
}

pub async fn race<A, B, F1, F2>(f1: F1, f2: F2) -> Either<A, B>
where
    F1: Future<Output = A>,
    F2: Future<Output = B>,
{
    let f1 = pin!(f1);
    let f2 = pin!(f2);
    match future::select(f1, f2).await {
        Either::Left((a, _f2)) => Either::Left(a),
        Either::Right((b, _f1)) => Either::Right(b),
    }
}
复制代码

 

这个Runtime是tokio单元包的成员,表示运行时(需要注意的是,rust反复强调运行时可以自建)

future::select 这个函数用于并发执行多个future,并返回一个future值。

四、简单执行async+await是什么样的?

前面的例子,有启用并发。 这也是常规的做法。

但是我也感兴趣,如果不用并发又是什么样的?

先说结论:就是什么也没有发生

复制代码
use std::thread;

struct Hero {
    name: String,
    age: u32,
}

fn main() {
    let hero = Hero {name: "孙悟空".to_string(),age: 100,};
    let ws = Hero {name: "武松".to_string(),age: 99,};
    let str1 = run(&hero);
    let str2 = run(&ws);
}

async fn fight(h: &Hero) -> String {
    let temp = h.name.clone() + "正在战斗中....";
    println!("{}", temp);
    temp.to_string()
}
//这个方法要返回实现Debug特质的类型
async fn run(h: &Hero) -> String {
    let r = fight(&h).await;
    println!("{}", r);
    r
}
复制代码

 

五、小结

  顺便也看了一些其它的资料。

  1. 创建异步函数的方式:函数(代码块)前加async,在函数体内添加await。 例外情况:不能把main标记为async
  2. 异步函数可以采用rust现有的两个异步运行时来运行:tokio或者async-std。似乎没有更多选择,此外为什么又那么多运行时,难道这个不应该是rust核心库的功能吗
  3. 异步的底层实现可能不一定是并发,也可能是并行,要看情况而定
  4. async函数默认返回的是 Future<Output = ()>。奇怪的语法又出现了,rust不要求你把返回结果显示地定义为Future。看第一个实例的page_title函数
    • 希望rust少一些这种默认的东西
    • 少一些故意和其它语言不同的东西
    • 此外,我也好奇,如果特意把返回类型写成Future会怎么样?
  5.  看了这些内容,距离异步编程还差99%
posted @   正在战斗中  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 普通人也能轻松掌握的20个DeepSeek高频提示词(2025版)
历史上的今天:
2024-01-16 spring与设计模式之五委派模式
点击右上角即可分享
微信分享提示