正确的方法是使用Arc<Mutex<...>>
或者,例如,Arc<RWLock<...>>
. Arc
是一个基于共享所有权的并发安全指针,指向不可变数据,并且Mutex
/RWLock
引入同步的内部可变性。然后你的代码将如下所示:
use std::sync::{Arc, Mutex};
use std::thread;
fn thread_func(results: Arc<Mutex<Vec<i32>>>, thread_id: i32) {
let mut results = results.lock().unwrap();
results[thread_id as usize] = thread_id;
}
fn foo() -> Arc<Mutex<Vec<i32>>> {
let results = Arc::new(Mutex::new(vec![0; 4]));
let guards: Vec<_> = (0..4).map(|i| {
let results = results.clone();
thread::spawn(move || thread_func(results, i))
}).collect();
for guard in guards {
guard.join();
}
results
}
不幸的是,这需要您返回Arc<Mutex<Vec<i32>>>
来自函数,因为无法“解开”该值。另一种方法是在返回之前克隆载体。
但是,使用像这样的板条箱作用域线程池 https://crates.io/crates/scoped_threadpool(其方法最近才变得合理;类似的东西可能会进入标准库,而不是现在已弃用的thread::scoped() http://doc.rust-lang.org/std/thread/fn.scoped.html函数,这是不安全的)它可以以更好的方式完成:
extern crate scoped_threadpool;
use scoped_threadpool::Pool;
fn thread_func(result: &mut i32, thread_id: i32) {
*result = thread_id;
}
fn foo() -> Vec<i32> {
let results = vec![0; 4];
let mut pool = Pool::new(4);
pool.scoped(|scope| {
for (i, e) in results.iter_mut().enumerate() {
scope.execute(move || thread_func(e, i as i32));
}
});
results
}
If your thread_func
需要访问整个向量,但是,如果没有同步,您就无法逃脱,因此您需要一个Mutex
,你仍然会遇到展开问题:
extern crate scoped_threadpool;
use std::sync::Mutex;
use scoped_threadpool::Pool;
fn thread_func(results: &Mutex<Vec<u32>>, thread_id: i32) {
let mut results = results.lock().unwrap();
result[thread_id as usize] = thread_id;
}
fn foo() -> Vec<i32> {
let results = Mutex::new(vec![0; 4]);
let mut pool = Pool::new(4);
pool.scoped(|scope| {
for i in 0..4 {
scope.execute(move || thread_func(&results, i));
}
});
results.lock().unwrap().clone()
}
但至少你不需要任何Arc
在这里。还execute()
方法是unsafe
如果您使用稳定编译器,因为它没有相应的修复程序来保证其安全。根据其构建脚本,它在高于 1.4.0 的所有编译器版本上都是安全的。