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

De Justine's wiki
Aller à la navigation Aller à la recherche
(Page créée avec « = A propos de cette page = Il s'agit de mes notes issues du tuto d'utilisation de l'environnement Tokio : [https://tokio.rs/tokio/tutorial ici] = Présentation = Tokio est un runtime asynchrone pour Rust, et fournit les outils pour créer des applications asynchrone utilisant le réseau. Les composants principaux sont: * Un runtime multi-process pour l'exécution de code asynchrone * Une version asynchrone de la librairie standard * Un grand ecosystème de li... »)
 
Aucun résumé des modifications
Ligne 33 : Ligne 33 :
  //Cargo.toml
  //Cargo.toml
  tokio = { version = "1", features = ["full"] }
  tokio = { version = "1", features = ["full"] }
  mini-redis = "0.4
  mini-redis = "0.4"
 
Puis dans le main.rs
<nowiki>
use mini_redis::{client, Result};
 
#[tokio::main]
async fn main() -> Result<()> {
    // Open a connection to the mini-redis address.
    let mut client = client::connect("127.0.0.1:6379").await?;
 
    // Set the key "hello" with value "world"
    client.set("hello", "world".into()).await?;
 
    // Get key "hello"
    let result = client.get("hello").await?;
 
    println!("got value from the server; result={:?}", result);
 
    Ok(())
}
</nowiki>
 
En faisant un cargo run avec le serveur mini-redis fonctionnel dans un autre terminal, on a bien le résultat attendu.
 
En détail :
let mut client = client::connect("127.0.0.1:6379").await?;
Fonction fournie par mini-redis qui donne un handle sur un client tcp. L'opération est asynchrone, mais le code ressemble à du code synchrone; on sait qu'il est asynchrone grace à "await".
 
== Programmation asynchrone ? ==
La plupart du temps, les programmes exécutés dans l'ordre dans lequel ils sont écrits. Si une tâche prend du temps, le thread est bloqué le temps que ça termine, ce qui peut être le cas pour une connexion TCP via laquelle un échange de données a lieu.
 
Avec la programmation asynchrone, les opération qui ne peuvent pas se terminer immédiatement vont en arrière plan. Le thread n'est pas bloqué et peut faire d'autres choses en attendant. Quand la tâche en arrière plan se termine, elle n'est plus suspendue et peut continuer. La programmation asynchrone peut permettre d'avoir des applications plus rapides, mais aussi bien plus compliquées. Elles forcent à gérer l'état des différentes tâches du programme.
 
== Compile-time green threading (j'ai pas envie de traduire) ==
Rust implémente l'asynchrone avec les mots async et await. Les fonctions qui font de l'asynchrone sont marquées avec async:
pub async fn connect<T: ToSocketAddrs>(addr: T) -> Result<Client> //etc
Les fonctions en "async fn" sont traduites par Rust lors de la compilation vers des routines asynchrones. N'importe quel appel à .await dans la fonction asynchrone renvoie le contrôle des opérations au thread, afin qu'il puisse faire autre chose pendant que les opérations se terminent en fond.
 
== Utiliser async / await ==
Les fonctions asynchrones sont appellées comme n'importe quelle autre fonction, mais ne renvoient pas une valeur représentant le résultat de leurs opérations. Elles renvoient une valeur qui représente l'opération. Il faut utiliser .await sur cette valeur afin d'obtenir le résultat.
Exemple:
 
<nowiki>
async fn say_world() {
    println!("world");
}
 
#[tokio::main]
async fn main() {
    // Calling `say_world()` does not execute the body of `say_world()`.
    let op = say_world();
 
    // This println! comes first
    println!("hello");
 
    // Calling `.await` on `op` starts executing `say_world`.
    op.await;
}
</nowiki>
 
Renvoie
hello
world

Version du 19 août 2024 à 14:17

A propos de cette page

Il s'agit de mes notes issues du tuto d'utilisation de l'environnement Tokio : ici

Présentation

Tokio est un runtime asynchrone pour Rust, et fournit les outils pour créer des applications asynchrone utilisant le réseau. Les composants principaux sont:

  • Un runtime multi-process pour l'exécution de code asynchrone
  • Une version asynchrone de la librairie standard
  • Un grand ecosystème de librairies associées.

Son rôle est avant tout d'accélerer les applications dans le cas où elles sont dépendante d'IO réseau en grande quantité, et pas dépendantes du CPU. Il n'est pas non plus intéressant pour accéder à un grand nombre de fichiers simultanément car les OS n'ont généralement pas d'API asynchrone pour les filesystems.

Setup

Le but du tutoriel est de montrer comment implémenter un client et un serveur Redis, avec un petit ensemble de commandes Redis. Ce projet s'appelle Mini-Redis et est sur Github.

Avec une version récente de Rust, on commence par le serveur mini-redis, qui nous permettra de tester notre client.

cargo install mini-redis
//Lancer le serveur
mini-redis-server
//Depuis un autre terminal
mini-redis-cli get foo
//Doit renvoyer (nil)

Hello Tokio

On commence par créer une application très simple, qui va se connecter au serveur mini-redis et passer la clef "hello" à "world".

cargo new my-redis
cd my-redis
//Cargo.toml
tokio = { version = "1", features = ["full"] }
mini-redis = "0.4"

Puis dans le main.rs

use mini_redis::{client, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // Open a connection to the mini-redis address.
    let mut client = client::connect("127.0.0.1:6379").await?;

    // Set the key "hello" with value "world"
    client.set("hello", "world".into()).await?;

    // Get key "hello"
    let result = client.get("hello").await?;

    println!("got value from the server; result={:?}", result);

    Ok(())
}

En faisant un cargo run avec le serveur mini-redis fonctionnel dans un autre terminal, on a bien le résultat attendu.

En détail :

let mut client = client::connect("127.0.0.1:6379").await?;

Fonction fournie par mini-redis qui donne un handle sur un client tcp. L'opération est asynchrone, mais le code ressemble à du code synchrone; on sait qu'il est asynchrone grace à "await".

Programmation asynchrone ?

La plupart du temps, les programmes exécutés dans l'ordre dans lequel ils sont écrits. Si une tâche prend du temps, le thread est bloqué le temps que ça termine, ce qui peut être le cas pour une connexion TCP via laquelle un échange de données a lieu.

Avec la programmation asynchrone, les opération qui ne peuvent pas se terminer immédiatement vont en arrière plan. Le thread n'est pas bloqué et peut faire d'autres choses en attendant. Quand la tâche en arrière plan se termine, elle n'est plus suspendue et peut continuer. La programmation asynchrone peut permettre d'avoir des applications plus rapides, mais aussi bien plus compliquées. Elles forcent à gérer l'état des différentes tâches du programme.

Compile-time green threading (j'ai pas envie de traduire)

Rust implémente l'asynchrone avec les mots async et await. Les fonctions qui font de l'asynchrone sont marquées avec async:

pub async fn connect<T: ToSocketAddrs>(addr: T) -> Result<Client> //etc

Les fonctions en "async fn" sont traduites par Rust lors de la compilation vers des routines asynchrones. N'importe quel appel à .await dans la fonction asynchrone renvoie le contrôle des opérations au thread, afin qu'il puisse faire autre chose pendant que les opérations se terminent en fond.

Utiliser async / await

Les fonctions asynchrones sont appellées comme n'importe quelle autre fonction, mais ne renvoient pas une valeur représentant le résultat de leurs opérations. Elles renvoient une valeur qui représente l'opération. Il faut utiliser .await sur cette valeur afin d'obtenir le résultat. Exemple:

async fn say_world() {
    println!("world");
}

#[tokio::main]
async fn main() {
    // Calling `say_world()` does not execute the body of `say_world()`.
    let op = say_world();

    // This println! comes first
    println!("hello");

    // Calling `.await` on `op` starts executing `say_world`.
    op.await;
}

Renvoie

hello
world