7. Threads, channels et processus en arrière-plan

Les programmes utilitaires qui tournent en arrière-plan doivent gérer la concurrence : surveiller des événements, répondre aux inputs utilisateur, et maintenir un état partagé entre plusieurs tâches. Rust fournit des primitives de concurrence sûres qui permettent d’écrire des programmes multi-thread sans data races. Dans cette unité, vous apprendrez à créer des threads, à les faire communiquer via des channels, et à partager de l’état avec Arc et Mutex.

 

Créer des threads avec std::thread

La bibliothèque standard de Rust fournit std::thread pour créer des threads système. La closure passée à thread::spawn doit utiliser move pour capturer les variables par valeur :

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(move || {
        for i in 0..5 {
            println!("Thread secondaire : {}", i);
            thread::sleep(Duration::from_millis(100));
        }
    });

    for i in 0..3 {
        println!("Thread principal : {}", i);
        thread::sleep(Duration::from_millis(150));
    }

    handle.join().unwrap();  // attendre que le thread termine
    println!("Thread secondaire terminé");
}

 

Communication avec les channels (mpsc)

Les channels permettent à des threads de s’envoyer des messages de façon sûre. Rust fournit des channels multi-producer, single-consumer (mpsc) : plusieurs émetteurs, un seul récepteur.

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

#[derive(Debug)]
enum MessageSurveillance {
    ReferenceDetectee(String),
    Arret,
}

fn main() {
    let (tx, rx) = mpsc::channel();
    let tx_clone = tx.clone();

    thread::spawn(move || {
        let refs = vec!["DOC-2024-001", "FACT-2023-042"];
        for r in refs {
            tx_clone.send(MessageSurveillance::ReferenceDetectee(r.to_string())).unwrap();
            thread::sleep(std::time::Duration::from_secs(1));
        }
        tx_clone.send(MessageSurveillance::Arret).unwrap();
    });

    loop {
        match rx.recv().unwrap() {
            MessageSurveillance::ReferenceDetectee(r) => println!("Détectée : {}", r),
            MessageSurveillance::Arret => { println!("Arrêt"); break; }
        }
    }
}

 

Partager l’état avec Arc<Mutex<T>>

Quand plusieurs threads doivent accéder au même état, on utilise Arc (Atomic Reference Counting, le pointeur partagé thread-safe) combiné avec Mutex (pour l’accès exclusif).

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let references = Arc::new(Mutex::new(Vec::<String>::new()));
    let mut handles = vec![];

    for i in 0..3 {
        let refs_clone = Arc::clone(&references);
        let handle = thread::spawn(move || {
            let ref_simulee = format!("DOC-2024-{:03}", i + 1);
            let mut guard = refs_clone.lock().unwrap();
            guard.push(ref_simulee);
        });
        handles.push(handle);
    }

    for h in handles { h.join().unwrap(); }
    println!("{:?}", references.lock().unwrap());
}

 

Concevoir une boucle événementielle

Pour un utilitaire en arrière-plan, on structure généralement le programme autour d’une boucle événementielle qui traite les messages reçus via un channel :

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

enum Evenement {
    ClipboardChange(String),
    RaccourciClavier(String),
    Quitter,
}

fn demarrer_surveillance(tx: mpsc::Sender<Evenement>) {
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_millis(500));
            // Ici : lire le clipboard avec la crate arboard
            // tx.send(Evenement::ClipboardChange(texte)).ok();
        }
    });
}

fn boucle_principale(rx: mpsc::Receiver<Evenement>) {
    loop {
        match rx.recv_timeout(Duration::from_secs(5)) {
            Ok(Evenement::ClipboardChange(t)) => println!("Clipboard: {}", t),
            Ok(Evenement::Quitter) => break,
            Ok(_) | Err(_) => {},
        }
    }
}
Points clés :

  • thread::spawn(move || ...) pour créer un thread qui capture son environnement
  • mpsc::channel() pour la communication inter-threads : tx.send() / rx.recv()
  • Arc<Mutex<T>> pour partager de l’état mutable entre threads en toute sécurité
  • Structurez les programmes en arrière-plan autour d’une boucle événementielle et de channels
SEE ALL Add a note
YOU
Add your Comment