En aquest tema, explorarem com gestionar la concurrència en Rust utilitzant fils. Els fils permeten executar múltiples tasques simultàniament, aprofitant millor els recursos del sistema i millorant el rendiment de les aplicacions.
Continguts
- Introducció als Fils
- Creació de Fils
- Comunicació entre Fils
- Sincronització de Fils
- Exercicis Pràctics
- Introducció als Fils
Els fils són una manera de dividir un programa en múltiples tasques que es poden executar simultàniament. En Rust, els fils són gestionats per la llibreria estàndard i proporcionen una manera segura i eficient de treballar amb concurrència.
Avantatges dels Fils
- Millor Utilització del Processador: Permet executar múltiples tasques en paral·lel.
- Rendiment Millorat: Pot reduir el temps d'execució de tasques complexes.
- Reactivitat: Millora la capacitat de resposta de les aplicacions.
Desavantatges dels Fils
- Complexitat: La gestió de fils pot ser complexa i propensa a errors.
- Condicions de Cursa: Problemes que poden sorgir quan múltiples fils accedeixen a dades compartides simultàniament.
- Creació de Fils
En Rust, podem crear fils utilitzant la funció thread::spawn. Aquesta funció pren una clojure que conté el codi que volem executar en un nou fil.
Exemple Bàsic
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("Hola des del fil secundari: {}", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("Hola des del fil principal: {}", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap();
}Explicació del Codi
thread::spawn: Crea un nou fil que executa la clojure passada com a argument.thread::sleep: Pausa l'execució del fil durant el temps especificat.handle.join(): Espera que el fil secundari acabi abans de continuar amb el fil principal.
- Comunicació entre Fils
Rust proporciona canals (channels) per a la comunicació segura entre fils. Els canals permeten enviar dades d'un fil a un altre.
Exemple de Canal
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("Hola");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("He rebut: {}", received);
}Explicació del Codi
mpsc::channel(): Crea un canal amb un transmissor (tx) i un receptor (rx).tx.send(val): Envia un valor a través del canal.rx.recv(): Rep un valor del canal.
- Sincronització de Fils
Per evitar condicions de cursa, Rust proporciona mecanismes de sincronització com Mutex i Arc.
Exemple amb Mutex i 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!("Resultat: {}", *counter.lock().unwrap());
}Explicació del Codi
Arc::newiArc::clone: Permet compartir dades entre fils de manera segura.Mutex::newicounter.lock(): Proporciona accés exclusiu a les dades compartides.
- Exercicis Pràctics
Exercici 1: Crear un Fil
Crea un programa que creï un fil que imprimeixi els números de l'1 al 5.
Exercici 2: Comunicació entre Fils
Crea un programa que utilitzi un canal per enviar un missatge d'un fil a un altre.
Exercici 3: Sincronització de Fils
Crea un programa que utilitzi Mutex i Arc per incrementar un comptador compartit entre múltiples fils.
Solucions
Solució Exercici 1
use std::thread;
fn main() {
let handle = thread::spawn(|| {
for i in 1..=5 {
println!("Fil: {}", i);
}
});
handle.join().unwrap();
}Solució Exercici 2
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let msg = String::from("Hola des del fil");
tx.send(msg).unwrap();
});
let received = rx.recv().unwrap();
println!("Missatge rebut: {}", received);
}Solució Exercici 3
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!("Comptador final: {}", *counter.lock().unwrap());
}Conclusió
En aquest tema, hem après com crear i gestionar fils en Rust, com comunicar-nos entre fils utilitzant canals, i com sincronitzar fils per evitar condicions de cursa. Aquests conceptes són fonamentals per escriure programes concurrents i eficients en Rust. En el proper tema, explorarem el passatge de missatges com una altra tècnica per gestionar la concurrència.
