multithreading - ¿Cómo comparto un objeto mutable entre hilos usando Arc?
concurrency rust (1)
La documentación de
Arc
dice:
Las referencias compartidas en Rust no permiten la mutación de forma predeterminada, y
Arc
no es una excepción: generalmente no puede obtener una referencia mutable a algo dentro de unArc
. Si necesita mutar a través de unArc
, useMutex
,RwLock
o uno de los tiposAtomic
.
Es probable que desee un
Mutex
combinado con un
Arc
:
use std::{
sync::{Arc, Mutex},
thread,
};
struct Stats;
impl Stats {
fn add_stats(&mut self, _other: &Stats) {}
}
fn main() {
let shared_stats = Arc::new(Mutex::new(Stats));
let threads = 5;
for _ in 0..threads {
let my_stats = shared_stats.clone();
thread::spawn(move || {
let mut shared = my_stats.lock().unwrap();
shared.add_stats(&Stats);
});
// Note: Immediately joining, no multithreading happening!
// THIS WAS A LIE, see below
}
}
Esto se basa principalmente en la documentación de
Mutex
.
¿Cómo puedo usar shared_stats después de for? (Estoy hablando del objeto Stats). Parece que shared_stats no se puede convertir fácilmente a Stats.
A partir de Rust 1.15, es posible recuperar el valor . Vea mi respuesta adicional para otra solución también.
[Un comentario en el ejemplo] dice que no hay subprocesos múltiples. ¿Por qué?
¡Porque me confundí! :-)
En el código de ejemplo, el resultado de
thread::spawn
(un
JoinHandle
) se descarta inmediatamente porque no está almacenado en ningún lado.
Cuando se cae el asa, el hilo se
separa
y puede o no terminar nunca.
Lo estaba confundiendo con
JoinGuard
, una antigua API eliminada que se
unió
cuando se descartó.
¡Perdón por la confusion!
Para un poco de editorial, sugiero evitar la mutabilidad por completo:
use std::{ops::Add, thread};
#[derive(Debug)]
struct Stats(u64);
// Implement addition on our type
impl Add for Stats {
type Output = Stats;
fn add(self, other: Stats) -> Stats {
Stats(self.0 + other.0)
}
}
fn main() {
let threads = 5;
// Start threads to do computation
let threads: Vec<_> = (0..threads).map(|_| thread::spawn(|| Stats(4))).collect();
// Join all the threads, fail if any of them failed
let result: Result<Vec<_>, _> = threads.into_iter().map(|t| t.join()).collect();
let result = result.unwrap();
// Add up all the results
let sum = result.into_iter().fold(Stats(0), |i, sum| sum + i);
println!("{:?}", sum);
}
Aquí, mantenemos una referencia a
JoinHandle
y luego esperamos a que finalicen todos los hilos.
Luego recopilamos los resultados y los sumamos todos.
Este es el patrón de
reducción de mapa
común.
Tenga en cuenta que ningún hilo necesita mutabilidad, todo sucede en el hilo maestro.
Estoy tratando de compartir un objeto mutable entre subprocesos en Rust usando
Arc
, pero recibo este error:
error[E0596]: cannot borrow data in a `&` reference as mutable
--> src/main.rs:11:13
|
11 | shared_stats_clone.add_stats();
| ^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
Este es el código de muestra:
use std::{sync::Arc, thread};
fn main() {
let total_stats = Stats::new();
let shared_stats = Arc::new(total_stats);
let threads = 5;
for _ in 0..threads {
let mut shared_stats_clone = shared_stats.clone();
thread::spawn(move || {
shared_stats_clone.add_stats();
});
}
}
struct Stats {
hello: u32,
}
impl Stats {
pub fn new() -> Stats {
Stats { hello: 0 }
}
pub fn add_stats(&mut self) {
self.hello += 1;
}
}
¿Que puedo hacer?