我是Rust的新手,并努力处理Rust中的所有包装器类型。我试图编写在语义上等于以下C代码的代码。代码尝试创建一个用于簿记的大表,但是会分割大表,以便每个线程只访问该表的本地小片。除非其他线程退出并且不再访问自己的切片,否则不会访问大表。
#include <stdio.h>
#include <pthread.h>
void* write_slice(void* arg) {
int* slice = (int*) arg;
int i;
for (i = 0; i < 10; i++)
slice[i] = i;
return NULL;
}
int main()
{
int* table = (int*) malloc(100 * sizeof(int));
int* slice[10];
int i;
for (i = 0; i < 10; i++) {
slice[i] = table + i * 10;
}
// create pthread for each slice
pthread_t p[10];
for (i = 0; i < 10; i++)
pthread_create(&p[i], NULL, write_slice, slice[i]);
for (i = 0; i < 10; i++)
pthread_join(p[i], NULL);
for (i = 0; i < 100; i++)
printf("%d,", table[i]);
}
如何使用Rust的类型和所有权来实现这一目标?
答案 0 :(得分:13)
让我们从代码开始:
// cargo-deps: crossbeam="0.1.6"
extern crate crossbeam;
const CHUNKS: usize = 10;
const CHUNK_SIZE: usize = 10;
fn main() {
let mut table = [0; CHUNKS * CHUNK_SIZE];
// Scoped threads allow the compiler to prove that no threads will outlive
// table (which would be bad).
crossbeam::scope(|scope| {
// Chop `table` into disjoint sub-slices.
for slice in table.chunks_mut(CHUNK_SIZE) {
// Spawn a thread operating on that subslice.
scope.spawn(move || write_slice(slice));
}
// `crossbeam::scope` ensures that *all* spawned threads join before
// returning control back from this closure.
});
// At this point, all threads have joined, and we have exclusive access to
// `table` again. Huzzah for 100% safe multi-threaded stack mutation!
println!("{:?}", &table[..]);
}
fn write_slice(slice: &mut [i32]) {
for (i, e) in slice.iter_mut().enumerate() {
*e = i as i32;
}
}
需要注意的是,这需要crossbeam
箱。 Rust 使用有一个类似的“范围”构造,但是在1.0之前发现了正确的健全漏洞,所以它被弃用了,没有时间来替换它。 crossbeam
基本上是替代品。
Rust允许你在这里做的是表达这样的想法,无论代码如何,在crossbeam::scoped
的调用中创建的线程的 none 都将在该范围内存活。因此,从外部借用的任何东西都会比线程更长寿。因此,线程可以自由地访问那些借用,而不必担心诸如一个线程超出堆栈帧table
定义并在堆栈上乱写的东西。
所以这应该与C代码或多或少相同,尽管没有唠叨担心你可能错过了什么。 :)
最后,使用scoped_threadpool
代替同样的事情。唯一真正的实际区别是,这允许我们控制使用的线程数。
// cargo-deps: scoped_threadpool="0.1.6"
extern crate scoped_threadpool;
const CHUNKS: usize = 10;
const CHUNK_SIZE: usize = 10;
fn main() {
let mut table = [0; CHUNKS * CHUNK_SIZE];
let mut pool = scoped_threadpool::Pool::new(CHUNKS as u32);
pool.scoped(|scope| {
for slice in table.chunks_mut(CHUNK_SIZE) {
scope.execute(move || write_slice(slice));
}
});
println!("{:?}", &table[..]);
}
fn write_slice(slice: &mut [i32]) {
for (i, e) in slice.iter_mut().enumerate() {
*e = i as i32;
}
}