For some quick context, I'm fairly new to Rust and decided to give myself a difficult-but-attainable task of implementing a sorting algorithm. I chose bogosort (or stupid-sort, shuffle-sort, whatever your preference is) and got it working pretty quickly.
I then decided to make it faster by threading it lol.
This works pretty well and I can definitely tell there's a performance improvement. However I want to make sure I'm implementing it decently. My biggest concern is making sure all of the other threads die when one of them finishes (i.e. "sorted" the vector).
The way I have it implemented below uses a boolean Mutex within an Arc. My understanding is that this way allows every thread to have access to it but also allows it to be modified safely. I use this Mutex as the condition in my while loop. Once any thread "sorts" the vector, it will change the Mutex - effectively telling every other thread to stop.
My question is, have I implemented this correctly? Am I stopping the threads correctly? And is the best way to do it? Any feedback (including constructively roasting my code) is immensely appreciated as I'd like to learn as much as I can.
use rand::prelude::*;
use rand_chacha::ChaCha8Rng;
use rand::thread_rng;
use std::time::Instant;
use std::thread;
use std::sync::mpsc;
use std::sync::{Mutex,Arc};
const ELEMENT_COUNT: i32 = 5;
const THREAD_COUNT: i32 = 5;
fn main() {
// Initialize a vector with <ELEMENT_COUNT> random numbers from 1-100
let mut rng = ChaCha8Rng::seed_from_u64(31);
let mut vec: Vec<i32> = (0..ELEMENT_COUNT).collect();
for x in &mut vec {
*x = rng.gen_range(1..100);
}
// Start a counter to track performance
let clock = Instant::now();
let (tx, rx) = mpsc::channel();
let mut handles = vec![];
let done = Arc::new(Mutex::new(true));
// Create a bunch of threads
for _i in 0..THREAD_COUNT {
let tx = tx.clone();
let done_shared = Arc::clone(&done);
let mut vec = Vec::clone(&vec);
let handle = thread::spawn(move || {
't_outer: while *done_shared.lock().unwrap() {
vec.shuffle(&mut thread_rng());
// println!("\nthread: {}\nvar: {:?}\n", i, *done_shared);
let mut counter = 1;
// Check if it's sorted
't_inner: for x in &vec {
// If the xth element is greater than the element after it: it's not sorted, so give up
if x > &vec[counter] {
break 't_inner;
}
counter = counter + 1;
// But, if this counter finds itself equal to the length of our vector, then we must be sorted
if counter == vec.len() {
tx.send(clock.elapsed()).unwrap();
// Set the while loop condition to false (effectively killing every other thread)
*done_shared.lock().unwrap() = false;
break 't_outer;
}
}
}
});
// handle.join();
handles.push(handle);
}
let b = rx.recv().unwrap();
println!("Took this long: {:?}", b);
// Join the handles in the vector
for i in handles {
i.join().unwrap();
}
}