I sometimes come across the problem of needing to terminate threads. I could use channels to signal threads to terminate. But I think I found an easier solution.
I use an Arc<()>
which I clone. One of those Arc
s stays in the controlling thread, and one of the Arc
s goes to the child thread.
When I want to terminate the child, I simply drop the Arc
. In the child thread, I check whether the Arc
's strong_count
is greater than 2
. If it's not, I terminate the child thread gracefully.
This is how it looks like:
use std::sync::Arc;
use std::thread::{sleep, spawn};
use std::time::Duration;
fn main() {
let keepalive_send = Arc::new(());
let keepalive_recv = keepalive_send.clone();
let join_handle = spawn(move || {
let mut counter = 0;
while Arc::strong_count(&keepalive_recv) > 1 {
// do something here:
counter += 1;
println!("running ({counter})...");
sleep(Duration::from_millis(150));
}
counter
});
// do something here:
sleep(Duration::from_millis(1000));
// terminate spawned thread:
drop(keepalive_send);
let result = join_handle.join().unwrap();
println!("Result: {result}");
}
I wonder: Is this a good approach to the problem? Or is there any easier or more idiomatic way?
(I think it should be pretty efficient. The strong_count
method just performs an atomic read with Acquire
ordering.)
I considered to use an AtomicBool
instead, but then I would need to implement a mechanism which ensures that the child gets notified to terminate if the controlling thread panics. By using the Arc
, this is already automatically happening because as soon as I drop the keepalive_send
, the child will terminate in the next loop iteration.