Rust Rayon ThreadPool:“不能借用可变的,因为它是 Fn 闭包中捕获的变量”

问题描述 投票:0回答:1

我正在尝试通过制作一个简单的向量加法函数来学习 Rust 的 Rayon 库。我当前的代码是这样的,假设

a
b
c
是初始化为相同长度的向量,
c
是可变的,
num_threads
是一个
usize
变量:

let pool = ThreadPoolBuilder::new().num_threads(num_threads).build()
           .expect("Could not create thread pool");
pool.install(|| {
    (0..c.len()).into_par_iter().for_each(|x| {
        c[x] = a[x] + b[x];
    });
});

但我收到错误

error[E0596]: cannot borrow c as mutable, as it is a captured variable in a Fn closure
    c[x] = a[x].wrap_add(b[x]);
    ^ cannot borrow as mutable

我还想指出,最终我的目标是制作基准测试软件,这就是为什么我使用线程池来指定线程数,但我认为

pool.install()
使用的额外闭包是问题根源的一部分。修改全局线程池也不是一个选项,因为只能完成一次,而且我想使用不同的线程数重新运行基准测试。我还想避免使用作用域 - 如果这是唯一的解决方案,那就这样吧 - 因为这会增加性能损失

我从根本上理解 Rayon 在这里不喜欢什么:Rust Book 的第 4.2 章说你不能对一个变量有多个可变引用,这本质上是每个线程都会得到的。然而,除了如何让这段代码运行的特定问题之外,这还引发了一些其他问题。

我似乎甚至无法将对

c
的引用移至线程池闭包中。为什么这是一个限制?当然,多线程的强大之处在于让多个线程同时对相关数据进行一些工作,那么为什么我不能以这种方式将数据传递给线程呢?

假设我可以将

c
的引用放入线程池中,是否有某种方法可以实现,例如,线程 0 获取
&mut c[0]
,线程 1 获取
&mut c[1]
,依此类推?如果 Rayon 的目的是抽象 Rust 基本多线程库的一些样板,那么 Rayon 不应该尝试让这变得更简单吗?

我看到的其他一些答案暗示迭代向量本身的内容会有所帮助,但由于我需要所有三个向量,所以我需要使用izip。这样做(用

(0..c.len().into_par_iter()...
替换
izip!(&a.mat, &b.mat, &mut c.mat).into_par_iter()...
)给了我错误

error[E0599]: the method into_par_iter exists for struct Map<Zip<Zip<Iter<'_, T>, Iter<'_, T>>, IterMut<'_, T>>, {[email protected]:303:9}>, but its trait bounds were not satisfied

izip!(&a.mat, &b.mat, &mut c.mat).into_par_iter().for_each(|x| {
   |                                                   ^^^^^^^^^^^^^
   |
   = note: the following trait bounds were not satisfied:
           `std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::ParallelIterator`
           which is required by `std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::IntoParallelIterator`
           `&std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::ParallelIterator`
           which is required by `&std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::IntoParallelIterator`
           `&mut std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::ParallelIterator`
           which is required by `&mut std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::IntoParallelIterator`

这似乎意味着引用现在已移至闭包中。是什么赋予了?为什么之前没有被移动?

multithreading rust threadpool rayon
1个回答
0
投票

我已经找到解决办法了。我需要通过在线程池的闭包中使用迭代器来更明确地借用

c
- 这似乎也解释了我的问题的“假设我可以......”部分 - 以及 @user2407038 的建议平行拉链:

let pool = ThreadPoolBuilder::new().num_threads(num_threads).build()
           .expect("Could not create thread pool");
pool.install(|| {
    (&mut c).into_par_iter().zip(0..c.len()).for_each(|x| {
        *x.0 = a[x.1] + b[x.1];
    });
});
© www.soinside.com 2019 - 2024. All rights reserved.