如果我有一个结构,就像:
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,所以函数不能在线程之间安全地共享。
在可以将Arc<dyn Fn(Vec<f64>) -> Vec<f64>包装在包装结构中,然后使用Sync使用unsafe impl标记包装的地方,就可以进行黑客攻击。但我想知道是否有比这更好的解决办法。
现在,由于函数在Arc中,所以我们可以保证函数值是不可变的。
我还可以保证,对于MyStruct的任何实例,向量的大小都是恒定的。它可能是2,3,或n,但它将是相同的。所以向量的大小是恒定的。所以函数的大小是事实上的常数。
事实上,如果我不使用Vec<f64>,而是使用&[f64]和[f64],那么这个函数仍然不会实现Send,即使片有一定的大小。
那么,为什么不能在线程之间共享这个函数,我能做些什么来在线程之间共享它呢?
发布于 2019-12-22 07:25:54
为了将Arc发送到另一个线程,Arc需要实现Send。如果你看一下the docs for Arc,你会发现它有
impl<T> Send for Arc<T> where
T: Send + Sync + ?Sized {}这意味着,要使代码工作,T (dyn Fn(Vec<f64>) -> Vec<f64>)需要实现Send和Sync。
因为你的类型是一个特征对象,所以你需要做的就是声明。
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>SyncSend如果没有Sync + Send特性,您的f函数就可以捕获对Cell的引用,这将导致竞争条件,因为多个线程可能同时尝试更新单元格的值。您的代码很可能没有这样做,但是您的start函数无法知道这一点,除非您告诉它f受到了足够的限制,不允许这样做。
https://stackoverflow.com/questions/59442080
复制相似问题