Concurrence et multithreading en Rust : de A à Z

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Concurrence et multithreading en Rust : de A à Z



Les applications modernes nécessitent de plus en plus une utilisation efficace des processeurs multicœurs. Rust propose une approche unique de la concurrence et du multithreading, alliant hautes performances et garanties de sécurité mémoire à la compilation. Dans cet article, nous aborderons les concepts clés : de la création de threads à l'utilisation de mutex et de canaux, avec des exemples de code pratiques.



1. Bases du multithreading : création et gestion des threads



En Rust, les threads sont créés à l'aide de la bibliothèque standard std::thread. Chaque thread s'exécute indépendamment, mais peut échanger des données avec d'autres threads.



Exemple simple de création d'un thread :



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

fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("Thread : nombre {}", i); thread::sleep(Duration::from_millis(1)); } });

for i in 1..5 { println!("Thread principal : nombre {}", i); thread::sleep(Duration::from_millis(1)); }

handle.join().unwrap(); // Attend la fin du thread}


Points clés :

  • thread::spawn prend une fermeture qui s'exécute dans un nouveau thread.
  • La méthode join bloque le thread actuel jusqu'à la fin du thread spécifié.
  • Sans join, le thread principal peut se terminer avant le thread enfant.


Pour transférer des données entre threads, utilisez les fermetures avec move :



use std::thread;

fn main() { let data = vec![1, 2, 3, 4, 5];

let handle = thread::spawn(move || { println!("Données du thread : {:?}", data); });

handle.join().unwrap(); // println!("{:?}", data); // Erreur ! data a été déplacé}


Le mot-clé move transfère la propriété des données à la fermeture, empêchant les courses de données à la compilation.



2. Échange sécurisé de données : mutex et verrous



Lorsque plusieurs threads accèdent à des données partagées, il existe un risque de course de données. Rust résout ce problème à l'aide de mutex (Mutex) et de types atomiques.



Exemple d'utilisation de Mutex avec Arc (compteur de références atomique) :



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

fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![];

for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); }

for handle in handles { handle.join().unwrap(); }

println!("Résultat : {}", *counter.lock().unwrap()); // 10}


Détails importants :

  • Arc<T> permet à plusieurs threads de posséder les mêmes données (clonage atomique).
  • Mutex::lock() retourne un verrou ; si le verrou est déjà pris, le thread attend.
  • Le verrou est automatiquement libéré à la sortie de la portée.


Pour un contrôle plus fin, utilisez RwLock, qui sépare les lecteurs et les rédacteurs :



use std::sync::RwLock;

let lock = RwLock::new(5);

// Lecture : plusieurs threads simultanément{ let r1 = lock.read().unwrap(); let r2 = lock.read().unwrap(); println!("Lecture : {} et {}", r1, r2);}

// Écriture : un seul thread{ let mut w = lock.write().unwrap(); *w += 1; println!("Écriture : {}", w);}


3. Canaux pour la transmission de messages



Alternative à la mémoire partagée — le modèle d'acteurs utilisant des canaux. Rust fournit un canal multi-producteur, mono-consommateur (mpsc).



Exemple de base :



u                

Blogs

Book Recommendations