En aquest tema, explorarem com gestionar l'estat compartit en programes concurrents en Rust. La concurrència és una característica poderosa que permet que múltiples parts d'un programa s'executin simultàniament, però també introdueix complexitat, especialment quan es tracta de compartir dades entre fils.
Objectius d'Aprenentatge
Al final d'aquest tema, hauràs après:
- Com utilitzar
Mutex
per protegir l'accés a dades compartides. - Com utilitzar
Arc
per compartir dades entre fils de manera segura. - Com evitar condicions de carrera i bloquejos.
- Introducció a
Mutex
Mutex
Un Mutex
(abreviatura de "mutual exclusion") és una estructura que garanteix que només un fil pugui accedir a una dada compartida en un moment donat. Això ajuda a evitar condicions de carrera, on múltiples fils accedeixen i modifiquen dades simultàniament de manera no segura.
Exemple Bàsic de Mutex
use std::sync::Mutex; fn main() { let m = Mutex::new(5); { let mut num = m.lock().unwrap(); *num = 6; } println!("m = {:?}", m); }
Explicació:
Mutex::new(5)
crea un nouMutex
que conté el valor 5.m.lock().unwrap()
bloqueja elMutex
per obtenir accés exclusiu a les dades. Si un altre fil ja ha bloquejat elMutex
, aquest fil esperarà fins que elMutex
estigui disponible.*num = 6
modifica el valor protegit pelMutex
.- Quan
num
surt de l'àmbit, elMutex
es desbloqueja automàticament.
- Compartir Dades entre Fils amb
Arc
Arc
Arc
(abreviatura de "atomic reference counting") és una estructura que permet compartir dades entre múltiples fils de manera segura. Arc
és similar a Rc
, però és segur per a l'ús concurrent.
Exemple de Arc
amb Mutex
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!("Result: {}", *counter.lock().unwrap()); }
Explicació:
Arc::new(Mutex::new(0))
crea unArc
que conté unMutex
inicialitzat a 0.Arc::clone(&counter)
crea una nova referència alArc
original, permetent que múltiples fils comparteixin el mateixMutex
.thread::spawn(move || { ... })
crea un nou fil que incrementa el valor protegit pelMutex
.handle.join().unwrap()
espera que cada fil acabi la seva execució.- Finalment, es mostra el valor del
counter
, que ha estat incrementat per cada fil.
- Evitar Condicions de Carrera i Bloquejos
Condicions de Carrera
Una condició de carrera ocorre quan múltiples fils accedeixen i modifiquen dades compartides simultàniament de manera no segura. L'ús de Mutex
ajuda a evitar aquestes condicions, però és important assegurar-se que tots els accessos a les dades compartides estiguin protegits per un Mutex
.
Bloquejos
Un bloqueig ocorre quan dos o més fils esperen indefinidament per recursos que estan bloquejats per altres fils. Per evitar bloquejos:
- Mantingues el codi dins d'una secció crítica (el codi que està protegit per un
Mutex
) tan curt com sigui possible. - Evita bloquejar múltiples
Mutex
al mateix temps.
Exercici Pràctic
Exercici
Crea un programa que utilitzi Arc
i Mutex
per compartir un vector entre múltiples fils. Cada fil ha d'afegir un número al vector.
Solució
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let data = Arc::new(Mutex::new(vec![])); let mut handles = vec![]; for i in 0..10 { let data = Arc::clone(&data); let handle = thread::spawn(move || { let mut vec = data.lock().unwrap(); vec.push(i); }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {:?}", *data.lock().unwrap()); }
Explicació:
Arc::new(Mutex::new(vec![]))
crea unArc
que conté unMutex
inicialitzat amb un vector buit.- Cada fil afegeix un número al vector protegit pel
Mutex
. - Finalment, es mostra el contingut del vector.
Resum
En aquest tema, hem après com utilitzar Mutex
per protegir l'accés a dades compartides i Arc
per compartir dades entre fils de manera segura. També hem discutit com evitar condicions de carrera i bloquejos. Aquests conceptes són fonamentals per escriure programes concurrents segurs i eficients en Rust.