rust学习十七.1、async和await

打印 上一主题 下一主题

主题 830|帖子 830|积分 2490

本文对应相关书籍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 的代
 
三、示例
  1. use trpl::{Either, Html};
  2. use std::thread;
  3. fn main() {
  4.     let args: Vec<String> = std::env::args().collect();
  5.     println!("{:?}", args);
  6.     //注意async关键字,并不会导致开启一个新的线程,仅仅意味着可以在内部使用await关键字等待异步操作完成。
  7.     trpl::run(async {
  8.         //lazy future 这里不会执行
  9.         let title_fut_1 = page_title(&args[1]);
  10.         //lazy future 这里不会执行
  11.         let title_fut_2 = page_title(&args[2]);
  12.         //race中的代码使用future::select执行并发,并返回最先完成的future。
  13.         //因为这里真正开始执行,所以才会在多次运行的时候,返回可能不同的结果
  14.         //race方法返回的是一个Either类型,Either是一个枚举类型,有两个值,Left和Right。
  15.         let (url, maybe_title) = match trpl::race(title_fut_1, title_fut_2).await {
  16.             Either::Left(left) => left,
  17.             Either::Right(right) => right,
  18.         };
  19.         println!("{url} returned first");
  20.         match maybe_title {
  21.             Some(title) => println!("Its page title is: '{title}'"),
  22.             None => println!("Its title could not be parsed."),
  23.         }
  24.     })
  25. }
  26. /**
  27. * 带了async的函数会返回一个future,这个future的类型是impl Future<Output = ()>,
  28. * output是一个泛型参数,这里是(),表示这个future的返回值是一个元组,
  29. */
  30. async fn page_title(url: &str) -> (&str, Option<String>) {
  31.     //这个语句会证实函数page_title是运行在主线程中...,并不是多线程的.
  32.     println!("{}",url);
  33.     println!("线程{:?}正在执行", thread::current().id());
  34.     let text = trpl::get(url).await.text().await;
  35.     let title = Html::parse(&text)
  36.         .select_first("title")
  37.         .map(|title| title.inner_html());
  38.     (url, title)
  39. }
复制代码
 
注意:第一次执行,你会怀疑是不是卡死了,这个不是问题,是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提供的示例代码,下面是感兴趣的部门:
  1. pub fn run<F: Future>(future: F) -> F::Output {
  2.     let rt = Runtime::new().unwrap();
  3.     rt.block_on(future)
  4. }
  5. pub async fn race<A, B, F1, F2>(f1: F1, f2: F2) -> Either<A, B>
  6. where
  7.     F1: Future<Output = A>,
  8.     F2: Future<Output = B>,
  9. {
  10.     let f1 = pin!(f1);
  11.     let f2 = pin!(f2);
  12.     match future::select(f1, f2).await {
  13.         Either::Left((a, _f2)) => Either::Left(a),
  14.         Either::Right((b, _f1)) => Either::Right(b),
  15.     }
  16. }
复制代码
 
这个Runtime是tokio单元包的成员,表示运行时(需要注意的是,rust反复强调运行时可以自建)
future::select 这个函数用于并发执行多个future,并返回一个future值。
四、简单执行async+await是什么样的?

前面的例子,有启用并发。 这也是通例的做法。
但是我也感兴趣,如果不消并发又是什么样的?
先说结论:就是什么也没有发生
  1. use std::thread;
  2. struct Hero {
  3.     name: String,
  4.     age: u32,
  5. }
  6. fn main() {
  7.     let hero = Hero {name: "孙悟空".to_string(),age: 100,};
  8.     let ws = Hero {name: "武松".to_string(),age: 99,};
  9.     let str1 = run(&hero);
  10.     let str2 = run(&ws);
  11. }
  12. async fn fight(h: &Hero) -> String {
  13.     let temp = h.name.clone() + "正在战斗中....";
  14.     println!("{}", temp);
  15.     temp.to_string()
  16. }
  17. //这个方法要返回实现Debug特质的类型
  18. async fn run(h: &Hero) -> String {
  19.     let r = fight(&h).await;
  20.     println!("{}", r);
  21.     r
  22. }
复制代码
 
五、小结

  顺便也看了一些别的的资料。

  • 创建异步函数的方式:函数(代码块)前加async,在函数体内添加await。 例外环境:不能把main标记为async
  • 异步函数可以接纳rust现有的两个异步运行时来运行:tokio或者async-std。好像没有更多选择,此外为什么又那么多运行时,岂非这个不应该是rust焦点库的功能吗
  • 异步的底层实现大概不一定是并发,也大概是并行,要看环境而定
  • async函数默认返回的是 Future。希奇的语法又出现了,rust不要求你把返回结果显示地定义为Future。看第一个实例的page_title函数

    • 希望rust少一些这种默认的东西
    • 少一些故意和别的语言不同的东西
    • 此外,我也好奇,如果特意把返回类型写成Future会怎么样?

  •  看了这些内容,距离异步编程还差99%

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

祗疼妳一个

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表