Rust - 将函数引用传递给线程

Sal*_*ler 1 multithreading thread-safety rust

假设我有一个类似的结构:

pub struct MyStruct {
    f: Arc<dyn Fn(Vec<f64>) -> Vec<f64>>,
}

impl MyStruct {
   pub fn new(f: Arc<dyn Fn(Vec<f64>) -> Vec<f64>>) -> MyStruct {
      MyStruct { f }
   }

   pub fn start(&self) {
      for _ in 0..5 {
        let f = self.f.clone();
        thread::spawn(move || {
            let v: Vec<f64> = get_random_vector();
            let v = (f)(v);
            // do something with v
        });
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

我收到一个错误,指出该函数无法在线程之间安全地共享,因为该dyn Fn(Vec<f64>) -> Vec<f64>)类型未实现Sync

我可以做一个黑客,我可以将 包装在 Wrapper 结构中,然后使用 usingArc<dyn Fn(Vec<f64>) -> Vec<f64>标记包装器。但我想知道是否有比这更好的解决方案。Syncunsafe impl

现在,由于函数位于 an 内部,Arc我们可以保证函数值是不可变的。

我还可以保证对于 的任何实例,向量的大小始终保持不变MyStruct。可能是 2、3,也n可能是相同的。所以向量的大小是恒定的。所以函数大小实际上是常数。

事实上,如果Vec<f64>我不使用&[f64]and [f64],该函数仍然不会实现Send,即使切片具有确定的大小。

那么为什么该函数不能在线程之间共享,我该怎么做才能在线程之间共享它呢?

log*_*yth 6

为了将 an 发送Arc到另一个线程,Arc需要实现Send. 如果您查看文档,Arc您会发现它有

impl<T> Send for Arc<T> where
    T: Send + Sync + ?Sized {}
Run Code Online (Sandbox Code Playgroud)

这意味着要使您的代码正常工作,您的T( dyn Fn(Vec<f64>) -> Vec<f64>) 需要实现SendSync

由于您的类型是一个特征对象,因此您需要做的是声明,例如

pub struct MyStruct {
    f: Arc<dyn Fn(Vec<f64>) -> Vec<f64> + Sync + Send>,
}

impl MyStruct {
   pub fn new(f: Arc<dyn Fn(Vec<f64>) -> Vec<f64> + Sync + Send>) -> MyStruct {
      MyStruct { f }
   }
   // ...
}
Run Code Online (Sandbox Code Playgroud)

如图所示,该T类型实现了所有这三个特征:

  • Fn(Vec<f64>) -> Vec<f64>
  • Sync
  • Send

如果没有该Sync + Send特征,您的f函数可能会捕获对 a 的引用Cell,这会导致竞争条件,因为多个线程可能会同时尝试更新单元格的值。您的代码很可能没有这样做,但是您的start函数无法知道这一点,除非您告诉它f受到足够的限制以不允许这样做。