How to move closures forever

核能气质少年 提交于 2021-02-05 12:11:49

问题


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

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