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。其他需要同时使用这段时间的函数同样处理。

原文地址:https://www.cnblogs.com/leemano/p/15752842.html