首页 > 解决方案 > 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
        });
      }
   }
}

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

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

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

我还可以保证向量的大小对于任何MyStruct. 它可能是 2、3,也可能n是相同的。所以向量的大小是恒定的。所以函数大小实际上是恒定的。

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

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

标签: multithreadingrustthread-safety

解决方案


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

impl<T> Send for Arc<T> where
    T: Send + Sync + ?Sized {}

这意味着要使您的代码正常工作,您的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 }
   }
   // ...
}

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

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

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


推荐阅读