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:
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.
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.
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.
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.
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.send
invia dati al canale, e recv
li riceve.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);
}
}
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
.
Una funzione dichiarata con async
restituisce un future, che rappresenta un valore calcolato in futuro.
async fn saluto() {
println!("Ciao, mondo asincrono!");
}
Per eseguire funzioni asincrone, utilizzi un runtime come tokio
.
#[tokio::main]
async fn main() {
saluto().await;
}
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);
}
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());
}
}
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!