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
Mutexper protegir l'accés a dades compartides. - Com utilitzar
Arcper compartir dades entre fils de manera segura. - Com evitar condicions de carrera i bloquejos.
- Introducció a
Mutex
MutexUn 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 nouMutexque conté el valor 5.m.lock().unwrap()bloqueja elMutexper obtenir accés exclusiu a les dades. Si un altre fil ja ha bloquejat elMutex, aquest fil esperarà fins que elMutexestigui disponible.*num = 6modifica el valor protegit pelMutex.- Quan
numsurt de l'àmbit, elMutexes desbloqueja automàticament.
- Compartir Dades entre Fils amb
Arc
ArcArc (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 unArcque conté unMutexinicialitzat a 0.Arc::clone(&counter)crea una nova referència alArcoriginal, 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
Mutexal 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 unArcque conté unMutexinicialitzat 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.
