0Pricing
Learn Rust Coding · Leçon

Canaux mpsc

Envoyer entre threads

Canaux mpsc est une leçon Learn Rust Coding gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Learn Rust Coding, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Learn Rust Coding comprend 4 leçons au total.

Qu’est-ce qu’un canal ?

Un canal est un conduit à sens unique qui permet d’envoyer des valeurs d’un thread à un autre. La bibliothèque standard de Rust fournit std::sync::mpsc, où mpsc signifie plusieurs producteurs, un seul consommateur.

  • La moitié Sender insère les valeurs.
  • La moitié Receiver récupère les valeurs.

Les canaux permettent aux threads de communiquer en transmettant des messages plutôt qu’en partageant directement la mémoire, ce qui évite de nombreuses courses aux données.

Créer un canal

Appelez mpsc::channel() pour obtenir un tuple (Sender, Receiver). Ici, nous envoyons une valeur depuis un thread créé à la volée vers le thread principal.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        tx.send(42).unwrap();
    });
    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

send et recv

tx.send(value) renvoie un Result : l’opération échoue uniquement si le récepteur a été détruit. rx.recv() bloque jusqu’à l’arrivée d’une valeur et renvoie Err lorsque tous les émetteurs ont disparu.

  • send transfère la propriété de la valeur au canal.
  • recv récupère la propriété de l’autre côté.
use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let msg = String::from("hello from thread");
        tx.send(msg).unwrap();
    });
    let text = rx.recv().unwrap();
    println!("{}", text);
}

La propriété transite par le canal

Comme send prend la valeur par propriété, vous ne pouvez plus l’utiliser après l’envoi. Cette règle vérifiée à la compilation garantit qu’aucun thread ne conserve une référence obsolète vers des données désormais détenues par un autre thread.

Ci-dessous, tenter d’afficher msg après send provoquerait une erreur de compilation : nous ne l’utilisons donc qu’une seule fois.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let data = vec![1, 2, 3];
        tx.send(data).unwrap();
        // data is moved; cannot use it here
    });
    let v = rx.recv().unwrap();
    println!("Sum: {}", v.iter().sum::<i32>());
}

Parcourir un récepteur

Un Receiver implémente IntoIterator. Le parcourir produit chaque valeur jusqu’à la fermeture du canal (lorsque tous les émetteurs ont été détruits). C’est la manière idiomatique de consommer un flux de messages.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        for i in 1..=3 {
            tx.send(i).unwrap();
        }
    });
    for received in rx {
        println!("Received: {}", received);
    }
}

Plusieurs producteurs avec clone

Le mp de mpsc signifie que vous pouvez avoir plusieurs émetteurs. Clonez le Sender et donnez-en une copie à chaque thread. Le récepteur collecte tout jusqu’à la destruction de chaque copie.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    let tx2 = tx.clone();
    thread::spawn(move || { tx.send("from A").unwrap(); });
    thread::spawn(move || { tx2.send("from B").unwrap(); });
    for msg in rx {
        println!("{}", msg);
    }
}

Sémantique de fermeture des canaux

La boucle du récepteur se termine automatiquement lorsque tous les émetteurs sont détruits. Si un seul Sender reste actif, for msg in rx bloque indéfiniment en attendant d’autres valeurs. Détruisez toujours les émetteurs ou limitez correctement leur portée pour permettre à la boucle de se terminer.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    let handle = thread::spawn(move || {
        for i in 0..3 {
            tx.send(i * 10).unwrap();
        }
        // tx dropped here, closing the channel
    });
    handle.join().unwrap();
    let total: i32 = rx.iter().sum();
    println!("Total: {}", total);
}

try_recv pour les lectures non bloquantes

recv bloque, mais try_recv renvoie immédiatement un Result. Il fournit Ok(value) si un message est prêt, ou Err si le canal est vide ou déconnecté. C’est utile dans les boucles d’événements qui doivent continuer à effectuer d’autres tâches.

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        thread::sleep(Duration::from_millis(50));
        tx.send("ready").unwrap();
    });
    loop {
        match rx.try_recv() {
            Ok(msg) => { println!("{}", msg); break; }
            Err(_) => println!("waiting..."),
        }
        thread::sleep(Duration::from_millis(20));
    }
}

Envoyer des types personnalisés

Tout type qui est Send peut circuler dans un canal, notamment vos propres structures et énumérations. Les énumérations sont idéales pour modéliser différents types de messages dans un protocole de travail.

use std::sync::mpsc;
use std::thread;

enum Job {
    Print(String),
    Add(i32, i32),
}

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        tx.send(Job::Print(String::from("hi"))).unwrap();
        tx.send(Job::Add(2, 3)).unwrap();
    });
    for job in rx {
        match job {
            Job::Print(s) => println!("print: {}", s),
            Job::Add(a, b) => println!("add: {}", a + b),
        }
    }
}

sync_channel et régulation de la pression

mpsc::sync_channel(n) crée un canal borné dont la taille de tampon est n. Lorsque le tampon est plein, send bloque jusqu’à ce qu’une place se libère. Cela fournit une régulation de la pression et empêche un producteur rapide de submerger un consommateur lent.

  • sync_channel(0) est un canal de rendez-vous : send et recv doivent se rencontrer.
use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::sync_channel(2);
    thread::spawn(move || {
        for i in 1..=4 {
            tx.send(i).unwrap();
            println!("sent {}", i);
        }
    });
    for v in rx {
        println!("got {}", v);
    }
}

Un modèle simple de travailleur

Les canaux sont particulièrement adaptés au modèle producteur/consommateur : un thread produit des éléments de travail et un autre les consomme et les traite. Ici, le thread principal produit des nombres et un travailleur élève chacun au carré, puis renvoie le résultat par un second canal.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (job_tx, job_rx) = mpsc::channel();
    let (res_tx, res_rx) = mpsc::channel();
    thread::spawn(move || {
        for n in job_rx {
            res_tx.send(n * n).unwrap();
        }
    });
    for n in 1..=4 {
        job_tx.send(n).unwrap();
    }
    drop(job_tx);
    for r in res_rx {
        println!("square: {}", r);
    }
}

Vérification rapide

Vérifiez votre compréhension des canaux mpsc.

Récapitulatif

Vous avez appris les principes fondamentaux des canaux mpsc :

  • mpsc::channel() renvoie une paire (Sender, Receiver).
  • send transfère une valeur au canal ; recv bloque pour la récupérer.
  • Parcourir un récepteur consomme les messages jusqu’à la destruction de tous les émetteurs.
  • Clonez le Sender pour avoir plusieurs producteurs.
  • try_recv est non bloquant  sync_channel(n) ajoute une régulation de la pression avec une capacité bornée.

Les canaux permettent aux threads de partager des données en toute sécurité en transférant la propriété plutôt qu’en partageant la mémoire.

Questions Fréquemment Posées

La leçon « Canaux mpsc » est-elle gratuite ?

Oui — le texte complet de « Canaux mpsc » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Learn Rust Coding, passe à CoddyKit PRO. Le cours Learn Rust Coding comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Canaux mpsc » ?

Envoyer entre threads Tu pratiques Learn Rust Coding avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Learn Rust Coding ?

Aucune expérience préalable n'est requise. Learn Rust Coding sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « Canaux mpsc » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Learn Rust Coding ?

Oui. Chaque leçon Learn Rust Coding inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Canaux mpsc
  2. Partager l’état avec Arc/Mutex
  3. Threads à portée limitée
  4. Canaux Crossbeam
← Retour à Learn Rust Coding