Most program we wrote is single threaded. Since most of the task is IO bound. It make sense to keep it simple by writing a single threaded program. But what if we have a CPU bound task? Let say, cracking a MD5 hash? Don't ask me why cracking MD5 hash, it's just one of CPU intensive task that I can think of, purely for demo purpose.
A naive hashing algorithm will be like this.
// Iterate from 0 util we found the target number
let mut i = 0_u32;
loop {
// Hash our guessing number and compare with the target hash
// Stop the loop if we found the target number
if md5::compute(i.to_string()).eq(&TARGET_HASH) {
break;
}
// What's our next guess?
i += 1;
}
For the hashing task we can distribute the work to N
threads. With each thread start search the target number starting from N
and continue the search by incrementing the number by N
.
So, for example we've 3 threads. The search space for each thread would be:
Thread 1: 1, 4, 7, 10...
Thread 2: 2, 5, 8, 11...
Thread 3: 3, 6, 9, 12...
That's how we divide the task to N
threads with each thread work on a unique and non-overlapping search space.
// A MPSC channel to inform the main thread some worker found the solution
let (tx, rx) = mpsc::channel();
print!("Parallel hashing with {} threads: ", num_workers);
// Spawn workers
for worker in 0..num_workers {
// Clone the transmission end and move it into the spawned worker
let tx1 = tx.clone();
thread::spawn(move || {
// The guess number start from the worker number
let mut i = worker;
// Iterate from 0 util we found the target number
loop {
// Hash our guessing number and compare with the target hash
let hash = md5::compute(i.to_string());
// Stop the loop if we found the target number
// And inform the main thread that we found the target number
if hash.eq(&TARGET_HASH) {
tx1.send("Found!").unwrap();
break;
}
// What's our next guess?
i += num_workers;
}
});
}
while let Ok(_) = rx.recv() {
// Exit main thread, this will kill all spawn threads
break;
}
Source code available on GitHub.
Top comments (0)