Multithreading 多个螺纹可以使用锈封吗?

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();

我希望能够让多个线程计算同一个闭包。我想到的应用程序是并行数值积分,因此函数域可以很容易地划分为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();
    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 { /* ... */ }
一个更标准的解决方案是使用作用域线程。这些线程保证在一定时间内退出,这允许您将超过线程生存期的引用传递给线程

另见:


crossbeam看起来就像我正在寻找的一样。一个问题:如果crossbeam基本上是因为可靠性问题而从Rust 1.0启动的,那么这些问题在现在的库中已经解决了吗?@JoshHansen是的。有很多背景信息供好奇者参考。相关的和相关的都非常完整。IIRC,有一个问题是f固定在铁锈适当允许其中一些是建立在顶部,但我现在找不到确切的链接。感谢额外的上下文。我最终使用横梁,这基本上似乎是工作。
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);
}