Rust - Pass a function reference to threads

断了今生、忘了曾经 提交于 2020-01-16 19:35:48

问题


Say I have a struct like:

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

I'm getting an error that the function cannot be shared between threads safely since the dyn Fn(Vec<f64>) -> Vec<f64>) type doesn't implement Sync.

There is a hack that I can do where I can wrap the Arc<dyn Fn(Vec<f64>) -> Vec<f64> in a Wrapper struct and then mark the wrapper with Sync using unsafe impl. But I was wondering if there is a better solution than that.

Now since the function is inside an Arc we have the guarantee that the function value is immutable.

I can also guarantee that the size of the vectors will always be constant for any instance of MyStruct. It might be 2, 3, or n but it will be the same. So the size of the vector is constant. So the function size is de facto constant.

In fact, if instead of Vec<f64> I use &[f64] and [f64], the function will still not implement Send, even though slices have a definite size.

So why can't the function be shared between threads and what can I do to indeed share it between threads?


回答1:


In order to send an Arc to another thread, the Arc needs to implement Send. If you look at the docs for Arc you can see that it has

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

which means that for your code to work, your T (dyn Fn(Vec<f64>) -> Vec<f64>) needs to implement Send and Sync.

Since your type is a trait object, what you need to do is declare that, e.g.

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 }
   }
   // ...
}

as in, the T type implements all three of these traits:

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

Without the Sync + Send trait, your f function could for instance capture a reference to a Cell, which would cause race conditions since multiple threads could potentially be trying to update the value of the cell at the same time. Your code may well not be doing that, but your start function has no way of knowing that unless you tell it that f is restricted enough to not allow that.



来源:https://stackoverflow.com/questions/59442080/rust-pass-a-function-reference-to-threads

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!