Rust offre potenti strumenti per la programmazione concorrente e asincrona. Con un focus sulla sicurezza e sull'assenza di condizioni di gara (race conditions), Rust garantisce un controllo rigoroso sul modo in cui i thread condividono e accedono ai dati. In questo articolo, scoprirai come:
- Creare e gestire thread.
- Comunicare tra thread in modo sicuro utilizzando i canali.
- Introdurre la programmazione asincrona con async/await.
Creare e Gestire Thread
Rust supporta la creazione di thread con la libreria standard attraverso il modulo std::thread. Ogni thread è una linea di esecuzione separata che può eseguire codice in parallelo.
Creazione di un Thread
Ecco un esempio base per creare un thread:
use std::thread;
fn main() {
let handle = thread::spawn(|| {
for i in 1..5 {
println!("Esecuzione del thread: {}", i);
}
});
for i in 1..5 {
println!("Esecuzione del thread principale: {}", i);
}
handle.join().unwrap(); // Attende che il thread termini
}
Il metodo thread::spawn avvia un nuovo thread, mentre handle.join() garantisce che il thread principale aspetti il completamento del thread figlio.
Passare Dati ai Thread
Puoi passare dati ai thread utilizzando move closure, che trasferisce la proprietà dei dati al thread:
fn main() {
let valori = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("Valori trasferiti: {:?}", valori);
});
handle.join().unwrap();
}
Il modificatore move è necessario per trasferire la proprietà della variabile valori al thread.
Comunicazione Sicura con i Canali
Rust offre i canali attraverso il modulo std::sync::mpsc (multiple producer, single consumer) per la comunicazione tra thread. I canali garantiscono una trasmissione sicura dei dati.
Esempio Base di Canali
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let messaggio = String::from("Ciao dal thread!");
tx.send(messaggio).unwrap();
});
let ricevuto = rx.recv().unwrap();
println!("Messaggio ricevuto: {}", ricevuto);
}
tx: trasmettitore.rx: ricevitore.sendinvia dati al canale, erecvli riceve.
Molteplici Produttori
Puoi clonare il trasmettitore per consentire a più thread di inviare dati:
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
let tx_clone = tx.clone();
thread::spawn(move || {
tx.send("Messaggio dal primo thread").unwrap();
});
thread::spawn(move || {
tx_clone.send("Messaggio dal secondo thread").unwrap();
});
for messaggio in rx {
println!("Ricevuto: {}", messaggio);
}
}
Introduzione ad Async/Await
Rust supporta la programmazione asincrona attraverso il modello async/await, che consente di scrivere codice non bloccante in modo semplice ed espressivo. Questa funzionalità è abilitata dal modulo async-std o tokio.
Scrivere una Funzione Asincrona
Una funzione dichiarata con async restituisce un future, che rappresenta un valore calcolato in futuro.
async fn saluto() {
println!("Ciao, mondo asincrono!");
}
Eseguire Funzioni Asincrone
Per eseguire funzioni asincrone, utilizzi un runtime come tokio.
#[tokio::main]
async fn main() {
saluto().await;
}
Esempio di Download Asincrono
Con reqwest, puoi eseguire operazioni di rete asincrone:
use reqwest;
#[tokio::main]
async fn main() {
let url = "https://jsonplaceholder.typicode.com/posts/1";
let risposta = reqwest::get(url).await.unwrap();
let contenuto = risposta.text().await.unwrap();
println!("Risposta: {}", contenuto);
}
Esercizi Pratici
-
Thread e Somma
Scrivi un programma che utilizzi più thread per calcolare la somma di una serie di numeri.Soluzione:
use std::thread; fn main() { let numeri = vec![1, 2, 3, 4, 5]; let handle = thread::spawn(move || { numeri.iter().sum::() }); let somma = handle.join().unwrap(); println!("Somma: {}", somma); } -
Canali e Filtro
Implementa un programma in cui un thread invia numeri e un altro li filtra, inviando solo i pari.Soluzione:
use std::sync::mpsc; use std::thread; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { for i in 1..10 { tx.send(i).unwrap(); } }); for valore in rx { if valore % 2 == 0 { println!("Pari: {}", valore); } } } -
Download Multiplo Asincrono
Utilizza async/await per scaricare più URL contemporaneamente.Soluzione:
use reqwest; #[tokio::main] async fn main() { let urls = vec![ "https://jsonplaceholder.typicode.com/posts/1", "https://jsonplaceholder.typicode.com/posts/2", ]; let futures = urls.iter().map(|&url| reqwest::get(url)); let risposte = futures::future::join_all(futures).await; for risposta in risposte { println!("Risposta: {}", risposta.unwrap().text().await.unwrap()); } }
Conclusione
La concurrency in Rust è sicura e potente grazie al controllo rigoroso sulla memoria e al supporto nativo per i thread e la programmazione asincrona. Nel prossimo articolo esploreremo la gestione degli arrori con funzioni come Panics, Option e Result. Prova gli esercizi proposti per approfondire i concetti e padroneggiare gli strumenti che Rust offre per la programmazione concorrente!
Lascia un commento