• Rust async 理解和初步编写


    Rust async 理解和初步编写

    运行环境:Win10 x64, rustc 1.57.0, 作者:乌龙哈里,日期:2021-12-31


    一、原始程序

    假设我们现在要做个饭,炉子只有一个灶头,步骤一般如下:
    1、洗锅、洗米等等,我们设为 fn prepare()
    2、开始煮饭。我们设为 fn cooking()
    3、洗菜等等,等饭煮好了才可以炒菜。我们设为 fn wash_vegetable()
    rust 程序如下:

    // #![allow(non_snake_case,unused)]
    use std::thread::sleep;
    use std::time::Duration;
    
    fn prepare(){
        //prepare before cooking rice
        for i in 1..6 {
            println!("prepare:{}",i);
            sleep(Duration::from_millis(500));
        }
    }
    
    fn cooking(){
        println!("rice cooking...");
        for i in (1..=10).rev(){
            println!("cooking...{}",i);
            sleep(Duration::from_millis(500));
        }
        println!("rice cooked!!!");
    }
    
    fn wash_vegetable(){
        for i in 101..106{
            println!("vegetable washing:{}",i);
            sleep(Duration::from_millis(500));
        }
    }
    
    fn main(){
        prepare();
        cooking();
        wash_vegetable();
    }
    
    /*output:
    prepare:1
    prepare:2
    prepare:3
    prepare:4
    prepare:5
    rice cooking...
    cooking...10
    cooking...9
    cooking...8
    cooking...7
    cooking...6
    cooking...5
    cooking...4
    cooking...3
    cooking...2
    cooking...1
    rice cooked!!!
    vegetable washing:101
    vegetable washing:102
    vegetable washing:103
    vegetable washing:104
    vegetable washing:105
    */

    很好的顺序执行。但是饭煮熟需要10个时间片段,这段时间上述例子是白白在等。能不能在饭煮熟的10个时间片段中先来洗洗菜?答案是能,需要并行。

    二、线程 thread

    并行分线程 thread 和异步 async 两种,我们先用熟悉的线程来操作。
    最简单的思路就是把在主程序 main() 中,把 fn coooking() 和 fn wash_vegetable() 这两个函数分别塞进两个线程,然后让线程跑起来就是了。需要用到 std::tread::spawn 这个函数。
    首先我们要把 use std::thread::sleep; 改成 use std::thread::{sleep,spawn};,然后就能愉快地去改main() 主函数了。具体改造见如下:

    use std::thread::{sleep,spawn};
    
    
    fn main(){
        prepare();
        //spawn
        //英 [spɔːn]
        //v.产卵;引发;引起;导致;造成
        //n.(鱼、蛙等的)卵
        let t1=spawn(cooking);
        let t2=spawn(wash_vegetable);
        t1.join().unwrap();
        t2.join().unwrap();
    }
    /*output:
    prepare:1
    prepare:2
    prepare:3
    prepare:4
    prepare:5
    rice cooking...
    cooking...9
    vegetable washing:101
    vegetable washing:102
    cooking...8
    vegetable washing:103
    cooking...7
    vegetable washing:104
    cooking...6
    vegetable washing:105
    cooking...5
    cooking...4
    cooking...3
    cooking...2
    cooking...1
    cooking...0
    rice cooked!!!
    */

    看见输出了,果然在煮饭的过程中插入洗菜了。
    引入线程按下面的套路写,就能跑起来了。

    1、let 线程名 = spawn( 函数名 );
    2、线程名.join();
    其实 spawn() 返回值是 JoinHandle<T>,先这么理解。

    这里要吐槽一下,网上好多教程都是复杂得一塌糊涂,对于我这个非科班的业余人士来说和看天书差不多。还是自己使劲啃,自己动手写才能理解,不需要那么高大上的例子。

    线程弄出来了,后面看了异步的种种,都在balabala...说线程很重等等,好吧,我们尝试来学习先进的技术吧。

    三、异步 async

    查了一下词典,async 应该是 asynchronous 或者是 asynchronization 的简写。 这个 async await 我记得是 C# 先引用的,现在 rust 也有了。好吧,我们开始学习。
    rust 最近的版本用的都是标准库 async_std,详细见 Crate async_stdasync-std。 我的基本理解是:async 和 await 其实是一种标记,需要在同步的函数前标记个 async,调用时末尾标记个.await。和 thread 的调用方式差不多。
    首先得改写工程项目的 cargo.toml :

    [dependencies]
    async-std = { version = "1.2.0", features = ["attributes"] }

    接着需要在程序头引用 use async_std::task::spawn;
    所以下面 main() 主函数需要加入编译条件:#[async_std::main]。改造具体如下:

    // ref:https://docs.rs/async-std/latest/async_std/index.html
    // cargo.toml
    // [dependencies]
    // async-std = { version = "1.2.0", features = ["attributes"] }
    use std::thread::sleep;
    use async_std::task::spawn;
    use std::time::Duration;
    
    fn prepare(){
        //prepare before cooking rice
        for i in 1..6 {
            println!("prepare:{}",i);
            sleep(Duration::from_millis(500));
        }
    }
    
    async fn cooking(){
        println!("rice cooking...");
        for i in (0..10).rev(){
            println!("cooking...{}",i);
            sleep(Duration::from_millis(500));
        }
        println!("rice cooked!!!");
    }
    
    async fn wash_vegetable(){
        for i in 101..106{
            println!("vegetable washing:{}",i);
            sleep(Duration::from_millis(500));
        }
    }
    
    #[async_std::main]
    async fn main(){
        prepare();
        let a1=spawn(cooking());
        let a2=spawn(wash_vegetable());
        a1.await;
        a2.await;
    }
    
    /*output:
    prepare:1
    prepare:2
    prepare:3
    prepare:4
    prepare:5
    rice cooking...
    cooking...9
    vegetable washing:101
    cooking...8
    vegetable washing:102
    cooking...7
    vegetable washing:103
    cooking...6
    vegetable washing:104
    cooking...5
    vegetable washing:105
    cooking...4
    cooking...3
    cooking...2
    cooking...1
    cooking...0
    rice cooked!!!
    */

    大功告成。终于简单的会用 async 了。
    总结一下用途就是,耗时过大的函数需要同步时,在其函数头前标记个 async。调用时在其调用者的 handle 后加个 .await。其他需要同时使用这段时间的函数同样处理。

  • 相关阅读:
    通过 AWS CLI 操作 AWS S3
    AWS S3文件存储服务器搭建(新)
    Vertx 接入MongoDB (九)
    Vertx 接入Redis (八)
    Vertx Mysql数据库优化 (七)
    Vertx 接入Mysql数据库 (六)
    Vertx 实现webapi实战项目(五)
    Vertx 实现webapi实战项目(四)
    Vertx 实现webapi实战项目(三)
    正则表达式:元字符(基本可用来匹配的字符)
  • 原文地址:https://www.cnblogs.com/leemano/p/15752842.html
Copyright © 2020-2023  润新知