Reputation: 3538
I have a structure that uses a limited form of dynamic programming. It maintains a cache of previously computed results to speed up later computations. I am currently storing it in a RefCell
. I recently started experimenting with multiple threads. Unfortunately I can not seem to Send
clone()
s of this data structure into a thread::spawn()
error[E0277]: `RefCell<DualKeyHashMap<search_context::CostKey, PlanCostLog>>` cannot be shared between threads safely
--> src/parallel_astar.rs:30:9
|
30 | std::thread::spawn(move || {
| ^^^^^^^^^^^^^^^^^^ `RefCell<DualKeyHashMap<search_context::CostKey, PlanCostLog>>` cannot be shared between threads safely
|
::: /home/thoth/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:624:8
|
624 | F: Send + 'static,
| ---- required by this bound in `spawn`
|
= help: within `SearchContext`, the trait `Sync` is not implemented for `RefCell<DualKeyHashMap<search_context::CostKey, PlanCostLog>>`
= note: required because it appears within the type `SearchContext`
= note: required because of the requirements on the impl of `Send` for `&SearchContext`
= note: required because it appears within the type `[closure@src/parallel_astar.rs:30:28: 42:10]`
Jmb's fix worked fine for my minimal example, but not on the full code, so I had to complicate the example:
use std::cell::RefCell;
use std::thread;
//#[derive(Clone)]
struct Inner {
pub val: i32,
}
//#[derive(Clone)]
struct Bacon {
count: RefCell<Inner>,
}
impl Bacon {
fn incr(&self) {
self.count.borrow_mut().val += 1;
}
fn get(&self) -> i32 {
self.count.borrow().val
}
}
fn go(bacon: &Bacon) {
let handles = (0..10)
.map(move |_| {
let b2 = bacon.clone();
thread::spawn(move || {
b2.incr();
println!("{}", b2.get())
})
})
.collect::<Vec<_>>();
for handle in handles {
handle.join().unwrap();
}
}
fn main() {
let bacon = Bacon {
count: RefCell::new(Inner{val:0}),
};
go(&bacon);
}
I can work around it by cloning the pieces of the SearchContext
and building a new one inside the thread. One oddity is that I can clone()
the field that is a RefCell
, and move
that into the thread to construct a new object (even though it is not necessary in my context).
Since some of the comments mention accessing the RefCell from multiple threads: I do not want to access the RefCell from multiple threads. That is why I am cloning and moving the data structure. Every thread should get its own indepent copy of Bacon
(or SearchContext
)
Upvotes: 1
Views: 337
Reputation: 3538
It turns out the real problem is that the Bacon
does not implement Clone
.
The error message the compiler gives does not point at this problem, so it took me a while to check for it. I'm not sure why it didn't give an error about the call to bacon.clone()
. Maybe the one error short-circuited error checking of the rest of the closure.
Upvotes: 1