004 Rust 异步编程,async await 的详细用法

我们之前简单介绍了async/.await的用法,本节将更详细的介绍async/.await。

async的用法

async主要有两种用法:async函数和asyn代码块(老版本书中说的是三种主要的用法,多了一个async闭包)。这几种用法都会返回一个Future对象,如下:

async fn foo() -> u8 { 5 }

fn bar() -> impl Future<Output = u8> {
    // `Future<Output = u8>`.
    async {
        let x: u8 = foo().await;
        x + 5
    }
}

fn baz() -> impl Future<Output = u8> {
    // implements `Future<Output = u8>`
    let closure = async |x: u8| {
        let y: u8 = bar().await;
        y + x
    };
    closure(5)
}

async转化的Future对象和其它Future一样是具有惰性的,即在运行之前什么也不做。运行Future最常见的方式是.await。

async的生命周期

考虑示例:

async fn foo(x: &u8) -> u8 { *x }

根据我们前面的知识,那么该函数其实等价于如下:

fn foo<'a>(x: &'a u8) -> impl Future<Output = ()> + 'a {
    async { *x }
}

这个异步函数返回一个Future对象。如果我们把这个Future对象在线程间进行传递,那么则存在生命周期的问题。如下:

//这种调用就会存在生命周期的问题
fn bad() -> impl Future<Output = ()> {
    let x = 5;
    foo(&x) // ERROR: `x` does not live long enough
}

正确的调用方式如下:

fn good() -> impl Future<Output = ()> {
    async {
        let x = 5;
        foo(&x).await;
    }
}

说明:通过将变量移动到async中,将延长x的生命周期和foo返回的Future生命周期一致。

async move

async 块和闭包允许 move 关键字,就像普通的闭包一样。一个 async move 块将获取它引用变量的所有权,允许它活得比目前的范围长,但放弃了与其它代码分享那些变量的能力。

示例如下

  • 配置文件:
//Cargo.toml中添加
[dependencies]
futures = "0.3.4
  • 源码:
//src/main.rs
use futures::executor;
async fn move_block() {
    let my_string = "foo".to_string();
    let f = async move {
        println!("{}", my_string);
    };
    // println!("{}", my_string); //此处不能再使用my_string
    f.await
}
fn main() {
    executor::block_on(move_block());
}

多线程

在使用多线程Future的excutor时,Future可能在线程之间移动,因此在async主体中使用的任何变量都必须能够在线程之间传输,因为任何.await变量都可能导致切换到新线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值