Concurrence et multithreading en Rust : sécurité et performance

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
```html

Concurrence et multithreading en Rust : écrire du code sûr et rapide



Les programmes modernes fonctionnent rarement en mode monothread. Le traitement des requêtes, le travail avec les fichiers, les opérations réseau — tout cela nécessite une exécution parallèle des tâches. Rust propose une approche unique de la concurrence : il garantit la sécurité mémoire au moment de la compilation, empêchant les courses de données. Dans cet article, nous allons voir comment travailler avec les threads, les canaux, les mutex et l'asynchronie en Rust.



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



En Rust, les threads sont créés via la bibliothèque standard std::thread. Chaque thread s'exécute indépendamment, mais le compilateur surveille strictement la durée de vie des variables.



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 : {}", i); thread::sleep(Duration::from_millis(100)); } });

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

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


Capture de la propriété dans les closures

Si un thread utilise des variables de la portée externe, il faut transférer la propriété via move :

let data = vec![1, 2, 3];

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

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


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



Pour un accès sécurisé aux données partagées depuis plusieurs threads, Rust utilise Mutex (mutex) en combinaison avec Arc (compteur de références atomique).



Exemple avec Mutex et Arc

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}


Important : Mutex se déverrouille automatiquement en sortant de la portée. Cela empêche le deadlock (interblocage) au niveau de la compilation.



RwLock — pour des lectures fréquentes

Si les données sont rarement modifiées mais souvent lues, utilisez RwLock :

use std::sync::RwLock;

let lock = RwLock::new(5);

{ let r1 = lock.read().unwrap(); let r2 = lock.read().unwrap(); // Lecture simultanée possible println!("{} {}", r1, r2);}

{ let mut w = lock.write().unwrap(); *w += 1; // L'écriture bloque la lecture}


3. Canaux : transmission de messages entre threads



Rust prend en charge le modèle des acteurs via les canaux (std::sync::mpsc). Cela permet aux threads d'échanger des données sans mémoire partagée.



Exemple avec un canal

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

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

thread::spawn(move || { let vals = vec!["hello", "from", "the", "thread"]; for val in vals { tx.send(val).unwrap(); thread::sleep(std::time::Duration::from_secs(1)); } });

for received in rx { println!("Reçu : {}", received); }}


Plusieurs expéditeurs

Avec mpsc::Sender, on peut cloner l'expéditeur pour plusieurs threads :

let (tx, rx) = mpsc::channel();let tx1 = tx.clone();

thread::spawn(move || { tx1.send("Thread 1").unwrap(); });thread::spawn(move || { tx.send("Thread 2").unwrap(); });

for received in rx { println!("Reçu : {}", received);}
```

Blogs

Book Recommendations