I have been interested in asynchronous programming (synchronous realization of asynchronous functions) since I got to know Go. Google learned that Rust also supports asynchronous programming. The main asynchronous programming libraries in Rust are Tokio and Async-STD. After scanning the document, the syntax is very similar. Tokio has long supported asynchronous programming features and is constantly iterating, and there are a number of frameworks that use Tokio. Async-std version 1.0 was released on September 26, 2019 and is very young. After reviewing some resources, the async-STD library was selected as the primary learning library for Rust asynchronous programming. The reason is simple:

  • Claims to be compliant with standard libraries,tokioSubsequent versions also lean in this direction
  • There is notokioHistorical baggage allows faster iteration of new features
  • thantokioLibraries are smaller and more powerful


Here’s an official example:

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

async fn read_file(path: &str) -> io::Result<String> {
    let mutfile = File::open(path).await? ;let mut contents = String::new();
    file.read_to_string(&mutcontents).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);
}
Copy the code

The main function of this example is to read the contents of the file and output to standard output. Asynchrony is embodied in:

  • Open files that are blocked voluntarily yield threads.File::open(path).await?
  • Read the contents of the file, blocking will also voluntarily free up the thread.file.read_to_string(&mut contents).await?


The core keywords of Rust asynchronous programming include:

  • Async: Builds oneFutureThe structure of the
  • .await: by waitingFuturereturn


Take a look at Future:

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

The core function is poll, which checks whether the Future can be returned. This function should not contain blocking function calls, but rather check the state of the task (a task is a block of code or function that needs to be executed). The return value of the function:

  • Poll: : Pending:futureNot ready
  • Poll: : Ready (val) :futureReady,valThe value istaskThe return value of.


If the future is not ready, the task needs to be put into the run-time system again for scheduling. Through the Walker function of the Context variable.

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

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


The whole process of asynC-STD asynchronous call is as follows:

  • throughasyncKey constructionfuturestructure
  • through.awaittask::spawnAnd so onfutureStructure associated functions or code blocks wrapped intotaskInto theRun-time systemschedule
  • Wait for the event to trigger. Such as socket read and write, file read and write, timer, signal, lock and so on
  • Associated with scheduling eventstask, and thefutureCheck the return value of the structure. If it isReady, returns the result, and the original execution flow continues; Otherwise, callContextthewakerFunction willtaskOnce again into theRun-time systemSchedule and wait for the next event to trigger.


reference

  • async-std

  • Future