« Rust : async » : différence entre les versions

De Justine's wiki
Aller à la navigation Aller à la recherche
(Page créée avec « = Exemple simple = Cargo.toml: <nowiki> [dependencies] futures = { version = "0.3.*" } tokio = {version = "0.2.*", features = ["full"] } </nowiki> Explications dans du code: <nowiki> use futures::prelude::*; use tokio::prelude::*; use tokio::task; //The principle of asynchronous programming is to get out of the usual way of programming, //where the program executes from start to finish, blocking on each task. If we have a web server //for example, we may nee... »)
 
Aucun résumé des modifications
Ligne 10 : Ligne 10 :
Explications dans du code:
Explications dans du code:


<nowiki>
<nowiki>
use futures::prelude::*;
use futures::prelude::*;
use tokio::prelude::*;
use tokio::prelude::*;

Version du 15 novembre 2023 à 16:18

Exemple simple

Cargo.toml:

[dependencies]
futures = { version = "0.3.*" }
tokio = {version = "0.2.*", features = ["full"] }

Explications dans du code:

use futures::prelude::*;
use tokio::prelude::*;
use tokio::task;

//The principle of asynchronous programming is to get out of the usual way of programming,
//where the program executes from start to finish, blocking on each task. If we have a web server
//for example, we may need to answer 10k requests at the same time.
//We could use threads, and Rust's fearless concurrency; but asynchronous programming is another
//way of doing things.
//
//So, we have asynchronous functions, that we can launch. They return a handler, and we can keep on
//executing code, coming back to the handler to see if it yields anything. Only in these
//asynchronous function can we use asynchronous things.
//
//In Rust, we need two things to do that : the "futures" crate, and a Runtime since Rust does not
//come with it by default. The most common on is Tokio, which we'll use here.
//
//We can then do 3 basic operations : start the runtime, spawn a future, and spawn CPU-blocking
//intensive operations.
//
//Of course, most work will happen in futures, this way. We need to be able to offset the execution
//of cpu-intensive things to other threads so we don't block at some point in our main thread, otherwise it is all
//useless.

type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;


//1 - Starting the runtime. This is the shortest version, using a trait
#[tokio::main]
async fn main() {
    //2.2 Here we finally get the result of it all.
    app().await.unwrap();
    //3.2 Here we execute our CPU-intensive future.
    otherapp().await.unwrap();
}

//1 bis - This is a more complicated way of doing the same:
//start the runtime, spawn a future, and block on it to get an answer.
fn othermain() {
    //runtime
    let mut rt = tokio::runtime::Runtime::new().unwrap();
    //future
    let future = app();
    //blocking
    rt.block_on(future);
}

async fn our_async_program() {
    println!("Hello world");
}

//2.1 - Here we spawn a future.
//We spawn it using 'task' so we can have multiple futures running at once.
//Note that this function is asynchronous, too.
async fn app() -> Result<()> {
    let join = task::spawn(our_async_program());
    let res = join.await?;
    Ok(())
}

//3.1 - Spawning CPU-intensive tasks.

//This fn is CPU-intensive.
fn fib_cpu_intensive(n: u32) -> u32 {
    match n {
        0 => 0,
        1 => 1,
        n => fib_cpu_intensive(n - 1) + fib_cpu_intensive(n - 2),
    }
}

//So we spawn a blocking future for it. 
//Note that this is also asynchronous.
async fn otherapp() -> Result<()>{
    let threadpool_future = task::spawn_blocking(||fib_cpu_intensive(30));
    //And await it.
    threadpool_future.await?;
    Ok(())
}