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::new
iArc::clone
: Permet compartir dades entre fils de manera segura.Mutex::new
icounter.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.