Ответ 1
Поддержка потоков в стандартной библиотеке позволяет созданным потокам пережить созданный ими поток; это хорошо! Однако если вы передадите ссылку на выделенную в стеке переменную одному из этих потоков, нет гарантии, что эта переменная будет действительна к тому моменту, когда поток выполнится. На других языках это позволило бы потоку обращаться к недопустимой памяти, создавая кучу проблем безопасности памяти.
К счастью, мы не ограничены стандартной библиотекой. По крайней мере два ящика предоставляют потоки с областью видимости - потоки, которые гарантированно завершат работу до окончания определенной области. Это может гарантировать, что переменные стека будут доступны на протяжении всего потока:
Существуют также ящики, которые абстрагируют низкоуровневые детали "потоков", но позволяют вам достичь ваших целей:
Вот примеры каждого. Каждый пример порождает несколько потоков и мутирует локальный вектор на месте без блокировки, без Arc
и без клонирования. Обратите внимание, что мутация имеет sleep
вызов, чтобы убедиться, что вызовы происходят параллельно.
Вы можете расширить примеры, чтобы поделиться ссылкой на любой тип, который реализует Sync
, например, Mutex
или Atomic*
. Однако их использование может привести к блокировке.
область видимости-ThreadPool
use scoped_threadpool::Pool; // 0.1.9
use std::{thread, time::Duration};
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
let mut pool = Pool::new(vec.len() as u32);
pool.scoped(|scoped| {
for e in &mut vec {
scoped.execute(move || {
thread::sleep(Duration::from_secs(1));
*e += 1;
});
}
});
println!("{:?}", vec);
}
коромысло
use crossbeam; // 0.6.0
use std::{thread, time::Duration};
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
crossbeam::scope(|scope| {
for e in &mut vec {
scope.spawn(move |_| {
thread::sleep(Duration::from_secs(1));
*e += 1;
});
}
})
.expect("A child thread panicked");
println!("{:?}", vec);
}
вискоза
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; // 1.0.3
use std::{thread, time::Duration};
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
vec.par_iter_mut().for_each(|e| {
thread::sleep(Duration::from_secs(1));
*e += 1;
});
println!("{:?}", vec);
}
клиент должен запаковать соединение в
Arc
когда оно является внутренним по отношению к библиотеке, код распараллеливается
Возможно, вы сможете скрыть свой параллелизм лучше? Не могли бы вы принять регистратор и затем обернуть его в Arc
/Mutex
прежде чем передать его своим потокам?