Ответ 1
Arc
документация гласит:
Совместно используемые ссылки в Rust по умолчанию не допускают мутации, и
Arc
не является исключением: обычно вы не можете получить изменяемую ссылку на что-то внутриArc
. Если вам нужно выполнить мутацию черезArc
, используйтеMutex
,RwLock
или один из типовAtomic
.
Скорее всего, вам понадобится Mutex
в сочетании с 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
}
}
Это в значительной степени взято из документации Mutex
.
Как я могу использовать shared_stats после for? (Я говорю об объекте Stats). Похоже, что shared_stats нельзя легко преобразовать в Stats.
Начиная с Rust 1.15, можно вернуть значение. Смотрите мой дополнительный ответ и для другого решения.
[Комментарий в примере] говорит, что многопоточности нет. Почему?
Потому что я запутался! :-)
В примере кода результат thread::spawn
(a JoinHandle
) немедленно удаляется, поскольку он нигде не сохраняется. Когда ручка уронена, нить отсоединяется и может или не может закончить. Я путал его с JoinGuard
, старым удаленным API, который включался при удалении. Извините за путаницу!
Для некоторой редакционной статьи я предлагаю полностью избежать изменчивости:
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);
}
Здесь мы сохраняем ссылку на JoinHandle
, а затем ждем завершения всех потоков. Затем мы собираем результаты и складываем их все. Это общая схема уменьшения карты. Обратите внимание, что никакой поток не нуждается в изменчивости, все это происходит в главном потоке.