Compartir estado con Arc/Mutex
Datos compartidos de forma segura
Compartir estado con Arc/Mutex es una lección gratuita de Learn Rust Coding en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Learn Rust Coding, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Learn Rust Coding incluye 4 lecciones en total.
Por qué el estado compartido es difícil
A veces el paso de mensajes no es suficiente y varios hilos necesitan realmente leer y escribir los mismos datos. Rust no permite compartir un valor mutable entre hilos sin protección, porque eso podría provocar una condición de carrera.
Las dos herramientas que se combinan son:
Arcpara compartir la propiedad entre hilos.Mutexpara realizar mutaciones seguras y exclusivas.
Rc no es seguro para hilos
Rc proporciona propiedad compartida, pero solo en un único hilo. Su contador de referencias no está sincronizado, por lo que el compilador se niega a enviarlo entre hilos. Para compartir entre varios hilos necesita Arc (conteo atómico de referencias).
Arc se comporta como Rc, pero actualiza su contador mediante operaciones atómicas, lo que hace que sus clones sean seguros entre hilos.
use std::sync::Arc;
fn main() {
let data = Arc::new(vec![1, 2, 3]);
let clone1 = Arc::clone(&data);
println!("original: {:?}", data);
println!("clone: {:?}", clone1);
println!("count: {}", Arc::strong_count(&data));
}Mutex proporciona acceso exclusivo
Un Mutex envuelve los datos y garantiza que solo un hilo acceda a ellos cada vez. Llame a .lock() para obtener un MutexGuard, que desreferencia al valor interno. El bloqueo se libera automáticamente cuando el guardián sale de ámbito.
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num += 10;
} // guard dropped here, lock released
println!("value = {:?}", m.lock().unwrap());
}Combinar Arc y Mutex
Para compartir datos mutables entre hilos, envuélvalos como Arc:
Arcpermite que muchos hilos posean un identificador de los mismos datos.Mutexpermite que cada hilo los modifique de forma segura, uno a la vez.
Clone el Arc para cada hilo antes de generarlo.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let c = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut n = c.lock().unwrap();
*n += 1;
});
handle.join().unwrap();
println!("counter = {}", *counter.lock().unwrap());
}Un contador compartido entre muchos hilos
El ejemplo clásico: diez hilos incrementan un contador compartido. Cada hilo tiene su propio clon de Arc y bloquea el Mutex para sumar uno. Después de unir todos los hilos, el total es exactamente 10 y no hay ninguna condición de carrera.
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 c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = c.lock().unwrap();
*num += 1;
}));
}
for h in handles {
h.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}El ámbito del bloqueo es importante
MutexGuard mantiene el bloqueo hasta que se elimina. Mantenerlo durante una tarea lenta bloquea a los demás hilos. Mantenga las secciones críticas cortas: bloquee, modifique y libere. Envolver el bloqueo en un bloque pequeño garantiza que se libere antes de realizar cualquier procesamiento adicional.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let log = Arc::new(Mutex::new(Vec::new()));
let mut handles = vec![];
for i in 0..3 {
let l = Arc::clone(&log);
handles.push(thread::spawn(move || {
{
let mut v = l.lock().unwrap();
v.push(i);
} // released quickly
}));
}
for h in handles { h.join().unwrap(); }
let mut result = log.lock().unwrap().clone();
result.sort();
println!("{:?}", result);
}Interbloqueos: un riesgo real
Un interbloqueo ocurre cuando dos hilos mantienen cada uno un bloqueo que el otro necesita y ambos esperan indefinidamente. Rust evita las condiciones de carrera, pero no los interbloqueos. Evítelos bloqueando siempre varios mutexes en el mismo orden y manteniendo los bloqueos breves.
Evite también bloquear dos veces el mismo Mutex en un hilo; el Mutex estándar no es reentrante.
Envenenamiento cuando un hilo entra en pánico
Si un hilo entra en pánico mientras mantiene un bloqueo, el Mutex queda envenenado. Las llamadas posteriores a .lock() devuelven Err, para indicar que los datos podrían ser incoherentes. Puede recuperar el guardián interno mediante into_inner() sobre el error si decide que los datos todavía se pueden utilizar.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(0));
let d = Arc::clone(&data);
let _ = thread::spawn(move || {
let mut g = d.lock().unwrap();
*g = 7;
panic!("boom"); // poisons the mutex
}).join();
match data.lock() {
Ok(g) => println!("ok: {}", *g),
Err(poisoned) => println!("recovered: {}", *poisoned.into_inner()),
}
}RwLock para muchos lectores
Cuando las lecturas superan ampliamente a las escrituras, un RwLock puede ser más rápido que un Mutex. Permite muchos lectores simultáneos o un único escritor. Utilice .read() para el acceso compartido y .write() para el acceso exclusivo.
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let config = Arc::new(RwLock::new(String::from("v1")));
let reader = Arc::clone(&config);
let r = thread::spawn(move || {
let val = reader.read().unwrap();
println!("read: {}", *val);
});
r.join().unwrap();
{
let mut w = config.write().unwrap();
*w = String::from("v2");
}
println!("final: {}", *config.read().unwrap());
}Atomics para contadores sencillos
Para un único contador entero, un Mutex completo es excesivo. Los tipos como AtomicUsize ofrecen actualizaciones sin bloqueos mediante métodos como fetch_add. Envuélvalos en Arc para compartirlos entre hilos. Elija un Ordering; SeqCst es el valor predeterminado seguro más sencillo.
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main() {
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
for _ in 0..5 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
c.fetch_add(1, Ordering::SeqCst);
}));
}
for h in handles { h.join().unwrap(); }
println!("count = {}", counter.load(Ordering::SeqCst));
}Elegir la herramienta adecuada
Orientación rápida para el estado compartido:
- Channel: transfiere la propiedad al estilo de una canalización.
- Arc<Mutex>: estructura mutable compartida, con lecturas y escrituras combinadas.
- Arc<RwLock>: datos compartidos con predominio de lecturas.
- Atomics: contadores o indicadores de un único tipo primitivo.
Prefiera la herramienta más sencilla que se ajuste al caso; recurra a los bloqueos solo cuando el paso de mensajes no represente bien el problema.
Comprobación rápida
Compruebe su comprensión del estado compartido.
Resumen
Ha aprendido a compartir el estado de forma segura entre hilos:
Arcpermite compartir la propiedad de forma segura entre hilos;Rcno.Mutexproporciona mutación exclusiva mediante un guardián que desbloquea automáticamente.Arc<Mutex<T>>es el patrón estándar para datos mutables compartidos.- Mantenga breves los ámbitos de los bloqueos y tenga cuidado con los interbloqueos y el envenenamiento.
RwLockes adecuado para datos con predominio de lecturas; los atómicos, para contadores sencillos.
Preguntas frecuentes
¿La lección «Compartir estado con Arc/Mutex» es gratis?
Sí — el texto completo de «Compartir estado con Arc/Mutex» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Learn Rust Coding, actualiza a CoddyKit PRO. El curso de Learn Rust Coding incluye 4 lecciones en total.
¿Qué aprenderé en «Compartir estado con Arc/Mutex»?
Datos compartidos de forma segura Practicas Learn Rust Coding con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Learn Rust Coding?
No se requiere experiencia previa. Learn Rust Coding en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Compartir estado con Arc/Mutex»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Learn Rust Coding?
Sí. Cada lección de Learn Rust Coding incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Canales mpsc
- Compartir estado con Arc/Mutex
- Hilos con ámbito
- Canales Crossbeam