I have this little program - essentially I want one thread to be able to tell the other to stop via a shared boolean in a struct.
use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;
use std::sync::atomic::{AtomicBool, Ordering};
struct Test {
should_stop: AtomicBool,
running_thread_handles: Vec<JoinHandle<()>>
}
impl Test {
fn new() -> Test {
Test {
should_stop: AtomicBool::new(false),
running_thread_handles: Vec::new()
}
}
fn stop(&mut self) {
self.should_stop.store(true, Ordering::Relaxed);
}
fn start(&mut self) {
let handle = thread::spawn(move || {
loop {
println!("Looping");
thread::sleep(Duration::from_millis(500));
// I want to effectively do this...
/*
if self.stop_bool.load(Ordering::Relaxed) {
println!("Stopping");
break;
}*/
}
});
self.running_thread_handles.push(handle);
}
}
impl Drop for Test {
fn drop(&mut self) {
self.stop();
// Here I want to iterate the self.running_thread_handles and
// make sure they are cleaned up
}
}
// I expect to see a 4 "Looping" messages and then a "Stopping" message
fn main() {
let mut test = Test::new();
test.start();
thread::sleep(Duration::from_millis(2000));
test.stop();
}
Perhaps there is a better approach altogether but I figure this is probably a good way to understand the lifetime stuff a bit.
I thought that all I need is an Arc
so I tried this:
fn start(&mut self) {
let stop_bool = Arc::new(&self.should_stop).clone();
let handle = thread::spawn(move || {
loop {
println!("Looping");
thread::sleep(Duration::from_millis(500));
if stop_bool.load(Ordering::Relaxed) {
println!("Stopping");
break;
}
}
});
self.running_thread_handles.push(handle);
}
That gives me this error:
error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
I figure the compiler does not understand the lifetime of the threads since all I am doing is storing the handles in a vector so I need to tell it somehow, but how?
If I declare the struct like this would I be getting nearer to making it work?
struct Test<'a> {
should_stop: AtomicBool,
running_thread_handles: &'a Vec<JoinHandle<()>>
}
I also have secondary, related, troubles that I cannot for the life of me figure out how to iterate through my vector of handles and call any functions on them in the Drop
impl. I suppose that the solution to that would be related, so I will not ask that.
There are two ways to access variables between threads:
Arc
):Borrowing is not supported by the Standard Library at the moment, although 3rd-party crates such as crossbeam offer it. For shared ownership, Arc
is indeed a possibility...
... however you need to consider what you put in Arc
carefully:
let stop_bool = Arc::new(&self.should_stop).clone();
Here, you are creating a Arc<&'a AtomicBool>
from a &'a self
, and thus you are sharing ownership over a borrowed reference. I'll point you back to the above explanation: cross-thread borrowing is not supported in the Standard Library yet.
You need a Arc<AtomicBool>
for proper shared ownership, and this is done by changing Test
:
struct Test {
should_stop: Arc<AtomicBool>,
running_thread_handles: Vec<JoinHandle<()>>
}
Then, cloning it is easy:
let stop_bool = self.should_stop.clone();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With