I would like to have a shared struct between threads. The struct has many fields that are never modified and a HashMap
, which is. I don't want to lock the whole HashMap
for a single update/remove, so my HashMap
looks something like HashMap<u8, Mutex<u8>>
. This works, but it makes no sense since the thread will lock the whole map anyways.
Here's this working version, without threads; I don't think that's necessary for the example.
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
fn main() {
let s = Arc::new(Mutex::new(S::new()));
let z = s.clone();
let _ = z.lock().unwrap();
}
struct S {
x: HashMap<u8, Mutex<u8>>, // other non-mutable fields
}
impl S {
pub fn new() -> S {
S {
x: HashMap::default(),
}
}
}
Is this possible in any way? Is there something obvious I missed in the documentation?
I've been trying to get this working, but I'm not sure how. Basically every example I see there's always a Mutex
(or RwLock
, or something like that) guarding the inner value.
Maybe you want to consider rust-evmap:
The trade-off is eventual-consistency: Readers do not see changes until the writer refreshes the map. A refresh is atomic and the writer decides when to do it and expose new data to the readers.
I don't see how your request is possible, at least not without some exceedingly clever lock-free data structures; what should happen if multiple threads need to insert new values that hash to the same location?
In previous work, I've used a
RwLock<HashMap<K, Mutex<V>>>
. When inserting a value into the hash, you get an exclusive lock for a short period. The rest of the time, you can have multiple threads with reader locks to theHashMap
and thus to a given element. If they need to mutate the data, they can get exclusive access to theMutex
.Here's an example:
This takes about 5.5 seconds to run on my machine, even though it starts 10 threads that each wait for 1 second while holding the exclusive lock to the element's data.
This solution isn't without issues, however. When there's a huge amount of contention for that one master lock, getting a write lock can take a while and completely kills parallelism.
I often advocate for performing some kind of smarter algorithm. For example, you could spin up N threads each with their own
HashMap
. You then shard work among them. For the simple example above, you could useid % N_THREADS
, for example. There are also complicated sharding schemes that depend on your data.As Go has done a good job of evangelizing: do not communicate by sharing memory; instead, share memory by communicating.