multithreading concurrency rust reference-counting

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 un Arc . Si necesita mutar a través de un Arc , use Mutex , RwLock o uno de los tipos Atomic .

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?