假设数据的键可以映射到一个。您可以拥有Arc>>>。
当您初始化数据结构时,请在将其放入Arc之前填充所有第一级映射(初始化后将不可变)。
当您需要从地图中获取值时,您需要执行双重获取,类似以下内容:
data.get(&map_to_u8(&key)).unwrap().lock().expect("poison").get(&key),其中解包是安全的,因为我们使用所有值初始化了第一个映射。
要在地图中写入类似以下内容:
data.get(&map_to_u8(id)).unwrap().lock().expect("poison").entry(id).or_insert_with(|| value);
很容易看出争用被减少了,因为现在我们有256个Mutex,多个线程请求相同的Mutex的概率较低。
@Shepmaster的示例在我的机器上使用100个线程需要约10秒钟,而以下示例需要略微超过1秒钟。
use std::{
collections::HashMap,
sync::{Arc, Mutex, RwLock},
thread,
time::Duration,
};
fn main() {
let mut inner = HashMap::new( );
for i in 0..=u8::max_value() {
inner.insert(i, Mutex::new(HashMap::new()));
}
let data = Arc::new(inner);
let threads: Vec<_> = (0..100)
.map(|i| {
let data = Arc::clone(&data);
thread::spawn(move || worker_thread(i, data))
})
.collect();
for t in threads {
t.join().expect("Thread panicked");
}
println!("{:?}", data);
}
fn worker_thread(id: u8, data: Arc<HashMap<u8,Mutex<HashMap<u8,Mutex<i32>>>>> ) {
loop {
if let Some(element) = data.get(&id).unwrap().lock().expect("poison").get(&id) {
let mut element = element.lock().expect("Mutex poisoned");
*element += 1;
thread::sleep(Duration::from_secs(1));
return;
}
thread::sleep(Duration::from_millis(50));
data.get(&id).unwrap().lock().expect("poison").entry(id).or_insert_with(|| Mutex::new(0));
}
}
Mutex
来避免RwLock
的开销? - Andrew