Rust 库 - async-std

3,471 阅读2分钟

自从接触Go后对异步编程(同步方式实现异步功能)比较感兴趣。Google后得知Rust也在大力支持异步编程,Rust中主流的异步编程库有:tokioasync-std。大致浏览文档后,发现两者的语法是很相似的。tokio很早就支持异步编程的特性,而且一直在迭代,也有大量的框架使用tokioasync-std1.0版本发布于2019年9月26日,可以说是非常年轻。查阅一些资料后,选择async-std库作为Rust异步编程的主要学习库。原因也很简单:

  • 声称兼容标准库,tokio后续的版本也往这方面靠
  • 没有tokio的历史包袱,可以更快的迭代新功能
  • tokio库更加小巧,功能也足够强大

下面看一个官方例子:

extern crate async_std;
use async_std::{fs::File, io, io::prelude::*, task};

async fn read_file(path: &str) -> io::Result<String> {
    let mut file = File::open(path).await?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).await?;
    Ok(contents)
}

fn main() {
     let reader_task = task::spawn(async {
        let result = read_file("./src/main.rs").await;
        match result {
            Ok(s) => println!("{}", s),
            Err(e) => println!("Error reading file: {:?}", e)
        }
    });

    task::block_on(reader_task);
}

这个例子主要的功能是:读取文件内容并输出到标准输出。异步体现在:

  • 打开文件如果被阻塞,会主动让出线程。File::open(path).await?
  • 读取文件内容,阻塞也会主动让出线程。file.read_to_string(&mut contents).await?

Rust异步编程的核心关键字包括:

  • async:构建一个Future 的结构
  • .await:通过等待Future返回

下面看一下Future:

pub trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
	...
}

核心函数是poll,主要作用是检查Future是否能够返回。此函数不应该包含阻塞的函数调用,而是检查task的状态(task是需要执行的代码块或函数)。函数的返回值:

  • Poll::Pending:future没有准备好
  • Poll::Ready(val):future准备好,val值则是task的返回值。

如果future没有准备好,则需要将task再次放进运行时系统进行调度。通过Context变量的walker函数进行。

pub struct Context<'a> {
    waker: &'a Waker,
    ...
}

impl<'a> Context<'a> {
    pub fn waker(&self) -> &'a Waker {
        &self.waker
    }
    
    ...
}

整个async-std异步调用的过程大概如下:

  • 通过async关键子构建future 结构
  • 通过.awaittask::spawn 等方法,将future结构关联的函数或代码块包装成task放入到运行时系统进行调度
  • 等待事件触发。如:套接字读写、文件读写、定时器、信号、锁等
  • 调度事件关联的task,并对future结构的返回值进行检查。如果是Ready,则返回结果,原来的执行流继续进行;否则,调用Contextwaker函数将task再次放入运行时系统进行调度,等待下一次事件触发。

参考