问题
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