Locking only one entry in HashMap

Is there some way to lock only one entry in a HashMap instead of the whole HashMap when using Mutexes or other concurrency locks?

Example of what I'd like to do:

let counter = Mutex::new(HashMap::new());
thread::scope(|scope| {
    for _ in 0..100 {
        scope.spawn(|| {
            let mut rng = rand::thread_rng();
            let random_number: u8 = rng.gen();
            // This is how it works now, so it locks the whole HashMap
            counter.lock().unwrap()
                .entry(random_number)
                .and_modify(|count| *count += 1)
                .or_insert(1);

            // And this is what I'd like to achieve, but of course this is not valid
            // counter.entry(random_number)
            //     .lock().unwrap()
            //     .and_modify(|count| *count += 1)
            //     .or_insert(1);
        });
    }
});

Instead of a Mutex<HashMap<K, V>>, use a HashMap<K, Mutex<V>>.

You won't be able to avoid an outer lock of some sort if you're inserting to the HashMap, since insertions can reallocate the backing storage and move everything around.

You could use a RwLock on the whole map and Mutexs on each element, then only lock the RwLock for reading until you know you need to do an insertion though.

RwLock<HashMap<K, Mutex<V>>>

2 Likes

If you need concurrent inserts, you need a concurrent hashmap rather than locking around a synchronous one. Actual concurrent hashmaps are a research topic on their own, but a simple enough pattern that gets you most of the way there is that implemented by dashmap -- shard your hashmap into multiple hashmaps via a tiered hash mapping scheme.

In a very simplistic brief, DashMap<K, V> is [RwLock<HashMap<K, V>>; SHARD_COUNT], and the hash bits determine which shard is locked before which bucket within the specific hash map.

5 Likes

Thank you, this is just what I was looking for!