Trying to wrap my head around how to insert things to a hasmap from different threads, but still couldn't get it.
Why is the following program on every execution missing some keys?
My current understanding is that .write wait to acquire the lock and .join wait for all threads to finish?
Thus I would expect to eventually all threads to insert their values into the hashmap, but obviously I am still missing something.
playground
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::RwLock;
use std::thread;
use std::vec;
pub fn main() {
let mut contacts: HashMap<String, String> = HashMap::new();
contacts.insert("main-thread".to_owned(), "hello world".to_owned());
let contacts = Arc::new(RwLock::new(contacts));
let mut children = vec![];
for _ in 0..10 {
let lock_contacts = Arc::clone(&contacts);
children.push(thread::spawn(move || {
let num = thread::current().id();
let num = format!("{num:?}");
if let Ok(mut contacts) = lock_contacts.write() {
contacts.insert(num.clone(), "hey".to_owned());
}
}));
}
let _ = children.into_iter().map(|c| c.join());
dbg!(contacts);
}
You are not actually joining threads before exiting from the main. And your culprit is this line.
let _ = children.into_iter().map(|c| c.join());
This line does nothing. There is no iteration, and that is because iterators in rust are lazy. If you would not discard value returned from the map, you would get a following warning.
warning: unused `Map` that must be used
--> src/main.rs:23:5
|
23 | children.into_iter().map(|c| c.join());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: iterators are lazy and do nothing unless consumed
= note: `#[warn(unused_must_use)]` on by default
help: use `let _ = ...` to ignore the resulting value
|
23 | let _ = children.into_iter().map(|c| c.join());
| +++++++
Note this part: iterators are lazy and do nothing unless consumed.
The solution is not to ignore this warning, but to instead iterate over the values and actually join threads. You can do it either by using a consuming iterator-adaptor like Iterator::for_each:
children.into_iter().for_each(|c| c.join().unwrap());
or simply using a for loop:
for child in children {
child.join().unwrap()
}
Your mistake was in this line:
let _ = children.into_iter().map(|c| c.join());
Recall from the Rust book that iterators are lazy. Iterator::map merely creates an adapter, but does not actually evaluate the values. Therefore your code is not actually synchronized.
To fix this, you can either:
Iterator::collect;for loop instead.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With