Rust async,看这一篇就够了~

Async

通过 async 标记的语法块会被转换成实现了Future特征的状态机

Future可以理解成就是Async修饰的函数

同步调用阻塞当前线程不同,在Async中, 具备(具体是否以异步非阻塞的方式运行取决于运行方式)异步非阻塞运行的能力,即在当前Future A执行并遇到阻塞时,A会让出当前线程的控制权,这样其它的Future就可以在该线程中运行,这种方式完全不会导致当前线程阻塞

一、创建和运行同步函数

先在package下的Cargo.toml下添加futures依赖

[dependencies]
futures = "0.3"

使用 async fn 语法来创建一个可以异步(具体是否异步取决于运行方式)的函数:

// `block_on`会阻塞当前线程直到指定的`Future`执行完成,这种阻塞当前线程以等待任务完成的方式较为简单、粗暴,
use futures::executor::block_on;

async fn hello_world() {
    println!("hello, world!");
}

fn main() {
    let future = hello_world(); // 返回一个Future, 因此不会打印任何输出
    block_on(future); // 执行`Future`并等待其运行完成,此时"hello, world!"会被打印输出
}

block_on同步的方式运行一个future(fn hello_world())),并会阻塞当前线程(上面代码就是主线程)直到指定的Future:(fn hello_world())执行完成,阻塞当前线程直到等待任务完成

二、异步非阻塞运行方式await

async 修饰的函数 才能调用await函数,async修饰函数表示这是一个新的future任务,可以在A函数中异步非阻塞的方式运行另一个B函数,

await意思是需要等待异步调用的完成,才能往下运行,但是如果await的函数发生了阻塞,可以去调用别的future

所有的future运行在同一个线程中。后面有例子进一步讲解,我们先看一个简单的熟悉一下语法

use futures::executor::block_on;

async fn hello_world() {
    hello_cat().await;//需要等待hello_cat执行完,才执行println!("hello, world!");
    //但是如果hello_cat()阻塞,可以去执行别的future,!!!而不是指往下执行println!("hello, world!")
    println!("hello, world!");
}

async fn hello_cat() {//因为hello_cat是async修饰的,所以hello_cat能调用await
    println!("hello, kitty!");
}
fn main() {
    let future = hello_world();
    block_on(future);
}

首先,整个代码运行在main线程这一个线程中,假设在hello_world()函数中,调用了hello_cat().await,表示异步非阻塞执行hello_cat函数,hello_cat也是一个future任务。执行完这个hello_cat函数,才可以继续执行hello_world()后续代码。

异步非阻塞的意思是,如果在hello_world()执行hello_cat中,hello_cat函数中发生阻塞行为,就不继续往下执行hello_world这个future。转头去执行别的future任务

如果hello_cat函数中没有发生阻塞行为,则按顺序执行完hello_cat,再往下执行 hello_world,运行期间,一直都是main线程在执行任务。

其实这里我有一个问题,多个future执行顺序是怎么样的,按创建顺序执行的吗?实践看看(默认是的)

三、进一步实践

实践讨论上面说的点

use std::time::Duration;
use tokio::time::sleep;

struct Song {
    author: String,
    name: String,
}

async fn learn_song() -> Song {
    println!("{}","1.我先学唱歌");//最先打印1-》我先学唱歌
    sleep(Duration::from_secs_f32(1 as f32)).await;//阻塞
    println!("{}","3.学唱歌小睡了一下,醒来了");//阻塞结束,第三个打印3-》学唱歌小睡了一下,醒来了
    Song {
        author: "坤坤".to_string(),
        name: String::from("《JntM》"),
    }
}

async fn sing_song() {
 
    println!(
        "4.唱一首,{}的 {} ~ {}",
        "vince", "fly", "music"
    );//第四个打印!!!
}

async fn dance() {//这是一个future B
	//阻塞等待,,再往下执行,打印“跳”
  sleep(Duration::from_secs_f32(0.01)).await;//阻塞事件只有0.01s,比learn_song早唤醒
    println!("2.跳");//所以第二个打印-》2.跳
}

async fn learn_and_sing() {//这是一个future A
   //在 learn_and_sing内,需要按照顺序执行learn_song和sing_song两个内部future A_A,A_B
   //先执行完learn_song,才能执行sing_song,learn_song内部有阻塞
    learn_song().await;//learn_song有阻塞时间,阻塞时间为1s,转头执行另一个Feature B
   //随着学唱歌结束,执行sing_song
    sing_song().await;
}

#[tokio::main]
async fn main() {
 
    let f1 = learn_and_sing();//这是一个future A,先执行这个
    let f2 = dance();//这也是一个future B,后执行这个
  
    futures::join!(f1, f2);
}

运行输出

1.我先学唱歌
2.跳
3.学唱歌小睡了一下,醒来了
4.唱一首,vince的 fly ~ music

详细解释都在代码中了

补充一下,在一个future A中,可以在future A中去执行别的future B,C ,但是这个是内部future,得保证在future A中,能运行到提交future B,C 的时候

1、block_up和.await的意义

async函数中,创建future任务需要执行,要么是block_up执行,要么是await执行,其实就是async函数的执行方式

  1. backup是同步执行
  2. await才是异步非阻塞(不阻塞主线程)

这里说的执行的意思有点像,将函数加入到监听事件队列里面,你不执行就不会监听,也就不会执行,

(1)没有使用await

我们可以看一段不加.await的代码

use std::time::Duration;
use tokio::time::sleep;

struct Song {
    author: String,
    name: String,
}

async fn learn_song() -> Song {
    println!("{}","1.我先学唱歌");

    println!("{}","3.学唱歌小睡了一下,醒来了");
    Song {
        author: "坤坤".to_string(),
        name: String::from("《JntM》"),
    }
}

async fn sing_song() {
  sleep(Duration::from_secs_f32(1 as f32)).await;
    println!(
        "4.唱一首,{}的 {} ~ {}",
        "vince", "fly", "music"
    );//第四个打印!!!
}

async fn dance() {//这是一个future B
	//阻塞等待,,再往下执行,打印“跳”
  sleep(Duration::from_secs_f32(0.01)).await;//阻塞事件只有0.01s,比learn_song早唤醒
    println!("2.跳");//所以第二个打印-》2.跳
}

async fn learn_and_sing() {//这是一个future A
   //在 learn_and_sing内,只有一个sing_song的future被监听,所以只有sing_song的运行结果
 
    learn_song();
   //随着学唱歌结束,执行sing_song
    sing_song().await;
}

#[tokio::main]
async fn main() {
 
    let f1 = learn_and_sing();//这是一个future A,先执行这个
    let f2 = dance();//这也是一个future B,后执行这个
  
    futures::join!(f1, f2);
}

运行结果

2.跳
4.唱一首,vince的 fly ~ music

(2)使用同步阻塞调用block_on

我们可以用block_on来执行

use std::time::Duration;
use tokio::time::sleep;
use futures::executor::block_on;
struct Song {
    author: String,
    name: String,
}

async fn learn_song() -> Song {
    println!("{}","1.我先学唱歌");
    sleep(Duration::from_secs_f32(3 as f32)).await;//大睡3s看看情况
    println!("{}","3.学唱歌小睡了一下,醒来了");
    Song {
        author: "坤坤".to_string(),
        name: String::from("《JntM》"),
    }
}

async fn sing_song() {
  sleep(Duration::from_secs_f32(1 as f32)).await;
    println!(
        "4.唱一首,{}的 {} ~ {}",
        "vince", "fly", "music"
    );//第四个打印!!!
}

async fn dance() {//这是一个future B
	//阻塞等待,,再往下执行,打印“跳”
  sleep(Duration::from_secs_f32(0.01)).await;//阻塞事件只有0.01s,比learn_song早唤醒
    println!("2.跳");//所以第二个打印-》2.跳
}

async fn learn_and_sing() {//这是一个future A
   //在 learn_and_sing内,只有一个sing_song的future被监听,所以只有sing_song的运行结果
 
   block_on(learn_song());
   //随着学唱歌结束,执行sing_song
    sing_song().await;
}

#[tokio::main]
async fn main() {
 
    let f1 = learn_and_sing();//这是一个future A,先执行这个
    let f2 = dance();//这也是一个future B,后执行这个
  
    futures::join!(f1, f2);
}

运行结果

1.我先学唱歌
3.学唱歌小睡了一下,醒来了
2.跳
4.唱一首,vince的 fly ~ music

欢迎查看我的博客
在这里插入图片描述

  • 30
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值