Multithreading 多个螺纹可以使用锈封吗?
我希望能够让多个线程计算同一个闭包。我想到的应用程序是并行数值积分,因此函数域可以很容易地划分为N个块并交给线程 这是一个简单的函数,它多次评估提供的闭包并平均结果:Multithreading 多个螺纹可以使用锈封吗?,multithreading,concurrency,closures,rust,Multithreading,Concurrency,Closures,Rust,我希望能够让多个线程计算同一个闭包。我想到的应用程序是并行数值积分,因此函数域可以很容易地划分为N个块并交给线程 这是一个简单的函数,它多次评估提供的闭包并平均结果: use std::sync::mpsc; use std::thread; const THREAD_COUNT: u64 = 4; fn average<F: Fn(f64) -> f64>(f: F) -> f64 { let (tx, rx) = mpsc::channel();
use std::sync::mpsc;
use std::thread;
const THREAD_COUNT: u64 = 4;
fn average<F: Fn(f64) -> f64>(f: F) -> f64 {
let (tx, rx) = mpsc::channel();
for id in 0..THREAD_COUNT {
let thread_tx = tx.clone();
thread::spawn(move || {
thread_tx.send(f(id as f64));
});
}
let mut total = 0.0;
for id in 0..THREAD_COUNT {
total += rx.recv().unwrap();
}
total / THREAD_COUNT as f64
}
fn main() {
average(|x: f64| -> f64 { x });
}
因此,我将+'static
添加到F
中,得到以下结果:
错误[E0382]:移动值的捕获:`f`
-->src/main.rs:11:28
|
10 |线程::繁殖(移动| |){
|------此处的值已移动(进入闭包)
11 |线程发送(f(id为f64));
|^移动后在此捕获的值
|
=注意:发生移动是因为'f'具有'f'类型,该类型不实现'Copy'特性
因此,我将+Copy
添加到F
并得到:
错误:类型未实现trait`core::marker::Copy``[closure@src/测试。rs:115:11:115:26]
似乎每个线程都想要自己的闭包副本(因为move
),但是闭包没有实现copy
,所以运气不好。我觉得很奇怪,因为如果闭包从来都不是变异状态,那么多线程访问它们会有什么安全问题
我可以通过提供一个常规函数而不是闭包来让代码正常工作,但这会使我的代码变得非泛型,也就是说,它只适用于特定的函数,而不适用于Fn(f64)的任何函数->f64
。对于我正在进行的集成类型,集成的函数通常有某些固定变量与集成变量混合,因此用闭包捕获固定变量似乎很自然
是否有某种方法可以使这种多线程函数求值以通用的方式工作?我只是在想问题吗?最终的问题是谁拥有闭包。编写的代码声明,闭包的所有权转移到
平均值
。此函数然后尝试给闭包to多个线程,正如您所看到的,它失败了,因为您不能将一个项目赋予多个子项
但是闭包没有实现Copy
,所以运气不好
截至,如果所有捕获的变量都实现了,则闭包确实实现了Clone
和Copy
。这意味着您的最后一个示例代码现在可以按原样工作:
fn average<F: Fn(f64) -> f64 + Send + 'static + Copy>(f: F) -> f64 { /* ... */ }
一个更标准的解决方案是使用作用域线程。这些线程保证在一定时间内退出,这允许您将超过线程生存期的引用传递给线程
另见:
use std::{
sync::{mpsc, Arc},
thread,
};
const THREAD_COUNT: u64 = 4;
fn average<F>(f: F) -> f64
where
F: Fn(f64) -> f64 + Send + Sync + 'static,
{
let (tx, rx) = mpsc::channel();
let f = Arc::new(f);
for id in 0..THREAD_COUNT {
let thread_tx = tx.clone();
let f = f.clone();
thread::spawn(move || {
thread_tx.send(f(id as f64)).unwrap();
});
}
let mut total = 0.0;
for _ in 0..THREAD_COUNT {
total += rx.recv().unwrap();
}
total / THREAD_COUNT as f64
}
fn main() {
average(|x| x);
}