Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Channels for passing hashmap between threads | stuck in loop | Rust

I am solving a problem for the website Exercism in rust, where I basically try to concurrently count how many times different letters occur in some text. I am doing this by passing hashmaps between threads, and somehow am in some kind of infinite loop. I think the issue is in my handling of the receiver, but I really don't know. Please help.

use std::collections::HashMap;
use std::thread;
use std::sync::mpsc;
use std::str;

pub fn frequency(input: &[&str], worker_count: usize) -> HashMap<char, usize> {
   
   // Empty case 
   if input.is_empty() {
       return HashMap::new();
   }

   // Flatten input, set workload for each thread, create hashmap to catch results
   let mut flat_input = input.join("");
   let workload = input.len() / worker_count;
   let mut final_map: HashMap<char, usize> = HashMap::new();

   
   let (tx, rx) = mpsc::channel();
   for _i in 0..worker_count {
       let task = flat_input.split_off(flat_input.len() - workload);
       let tx_clone = mpsc::Sender::clone(&tx);

       
       // Separate threads ---------------------------------------------
       thread::spawn(move || {
          let mut partial_map: HashMap<char, usize> = HashMap::new();
          for letter in task.chars() {
              match partial_map.remove(&letter) {
                  Some(count) => {
                      partial_map.insert(letter, count + 1);
                  },
                  None => {
                      partial_map.insert(letter, 1);
                  }
              }
          }
          tx_clone.send(partial_map).expect("Didn't work fool");
       });
       // --------------------------------------------------  
       
   }
   
   // iterate through the returned hashmaps to update the final map
   for received in rx {
       for (key, value) in received {
           match final_map.remove(&key) {
               Some(count) => {
                   final_map.insert(key, count + value);
               },
               None => {
                   final_map.insert(key, value);
               }  
           }
       }
   }
   
   return final_map;
}
like image 374
Benjamin Peinhardt Avatar asked Oct 31 '25 15:10

Benjamin Peinhardt


1 Answers

Iterating on the receiver rx will block for new messages while senders exist. The ones you've cloned into the threads will drop out of scope when they're done, but you have the original sender tx still in scope.

You can force tx out of scope by dropping it manually:

for _i in 0..worker_count {
    ...
}

std::mem::drop(tx); // <--------

for received in rx {
    ...
}

like image 61
kmdreko Avatar answered Nov 02 '25 08:11

kmdreko