问题
I'm designing a little struct that runs closures for me and I can set them to stop:
pub fn run(&self, f: Box<dyn Fn()>) {
let should_continue = self.should_continue.clone();
self.run_thread = Some(std::thread::spawn(move || {
while should_continue.load(Ordering::Relaxed) {
//f should run fast so `should_continue` is readed frequently
f();
}
}));
}
as you can see, I'm passing Fn
in a box, which gives me an error about Box not being shareable between threads. Actually, I don't care about fn once I pass it to this function run
, so I wanted to move the closure to this function, since I'll not use it anymore. I cannot mark Fn
as send because the f
that I'm gonna actually pass does not implement Send
.
So, how can I move a closure completely?
//move this closure to inside of run
self.run(||{});
回答1:
Having a buildable reproduction case rather than code with random unprovided dependencies is useful so here's what I understand of your code.
The error I get is that the dyn Fn
can not be sent between threads which is very different than shared: while there are many things which can not be shared (Sync
) between threads (they can only be used from one thread at a time) there are also things which must remain on their original thread at all time. Rc
for instance, is not Send
, because it's not a thread-safe reference-counted pointer sending an Rc
to a different thread would break its guarantees, therefore that's not allowed.
dyn Fn
is opaque and offers no real guarantee as to what it's doing internally except for, well, being callable multiple times. So as far as the compiler is concerned it could close over something which isn't Send
(e.g. a reference to a !Sync
type, or an Rc
, ...), which means the compiler assumes the Fn
isn't Send
either.
The solution is simple: just define f: Box<dyn Fn() + Send>
, this way within run
you guarantee that the function can, in fact, be sent between threads; and the caller to run
will get an error if they're trying to send a function which can not be sent.
demo
run_ok
uses a trivial closure, there is no issue with sending it over. run_not_ok
closes over an Rc
, and the function therefore doesn't compile (just uncomment it to see). run_ok2
is the same function as run_not_ok
using an Arc
instead of the Rc
, and compiles fine.
来源:https://stackoverflow.com/questions/64151389/how-to-move-closures-forever