我想在线程之间有一个共享结构。该结构有许多永远不会被修改的字段和一个HashMap
,它是。我不想锁定整个HashMap
以进行单次更新/删除,因此我的HashMap
看起来像HashMap<u8, Mutex<u8>>
。这是有效的,但没有任何意义,因为线程无论如何都会锁定整个地图。
这是这个工作版本,没有线程;我不认为这个例子是必要的。
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(),
}
}
}
这有可能吗?我在文档中遗漏了一些明显的东西吗?
我一直在尝试让这个工作,但我不知道如何。基本上我看到的每个例子总是有一个Mutex
(或RwLock
,或类似的东西)来保护内在价值。
答案 0 :(得分:1)
答案 1 :(得分:0)
假设数据键可映射到u8
您可以拥有Arc<HashMap<u8,Mutex<HashMap<Key,Value>>>
初始化数据结构时,先将所有第一级贴图填充到Arc中(初始化后将不可变)
当您想要从地图中获取一个值时,您将需要进行两次获取,例如:
data.get(&map_to_u8(&key)).unwrap().lock().expect("poison").get(&key)
unwrap
是安全的,因为我们使用所有值初始化了第一个映射。
在地图上写类似的内容:
data.get(&map_to_u8(id)).unwrap().lock().expect("poison").entry(id).or_insert_with(|| value);
很容易看到争用减少了,因为我们现在有256个Mutex,并且多个线程询问同一个Mutex的可能性很低。
@Shepmaster示例具有100个线程在我的计算机上花费大约10s,下面的示例花费了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 {
// first unwrap is safe to unwrap because we populated for every `u8`
if let Some(element) = data.get(&id).unwrap().lock().expect("poison").get(&id) {
let mut element = element.lock().expect("Mutex poisoned");
// Perform our normal work updating a specific element.
// The entire HashMap only has a read lock, which
// means that other threads can access it.
*element += 1;
thread::sleep(Duration::from_secs(1));
return;
}
// If we got this far, the element doesn't exist
// Get rid of our read lock and switch to a write lock
// You want to minimize the time we hold the writer lock
// We use HashMap::entry to handle the case where another thread
// inserted the same key while where were unlocked.
thread::sleep(Duration::from_millis(50));
data.get(&id).unwrap().lock().expect("poison").entry(id).or_insert_with(|| Mutex::new(0));
// Let the loop start us over to try again
}
}