我不明白你的请求是怎么可能的,至少如果没有一些非常聪明的无锁数据 struct ;如果多个线程需要插入散列到同一位置的新值,该怎么办?
在之前的工作中,我使用了RwLock<HashMap<K, Mutex<V>>>
.当向散列中插入一个值时,会得到一个短时间的独占锁.剩下的时间里,你可以有多个线程,读卡器锁定HashMap
,从而锁定一个给定的元素.如果他们需要对数据进行变异,他们可以独占访问Mutex
.
下面是一个例子:
use std::{
collections::HashMap,
sync::{Arc, Mutex, RwLock},
thread,
time::Duration,
};
fn main() {
let data = Arc::new(RwLock::new(HashMap::new()));
let threads: Vec<_> = (0..10)
.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<RwLock<HashMap<u8, Mutex<i32>>>>) {
loop {
// Assume that the element already exists
let map = data.read().expect("RwLock poisoned");
if let Some(element) = map.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
drop(map);
let mut map = data.write().expect("RwLock poisoned");
// 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));
map.entry(id).or_insert_with(|| Mutex::new(0));
// Let the loop start us over to try again
}
}
在我的机器上运行大约需要2.7秒,即使它启动10个线程,每个线程在保持对元素数据的独占锁的同时等待1秒.
然而,这个解决方案并非没有问题.当对一个主锁存在大量争用时,获取写锁可能需要一段时间,并完全 destruct 并行性.
在这种情况下,你可以切换到RwLock<HashMap<K, Arc<Mutex<V>>>>
.一旦有了读或写锁,就可以克隆值的Arc
,返回它并解锁hashmap.
下一步是使用arc-swap这样的 crate ,上面写着:
然后锁定、克隆[RwLock<Arc<T>>
]并解锁.这会受到CPU级别争用(在Arc
的锁和引用计数上)的影响,这使得它相对较慢.根据具体的实现,一个更新可能会被稳定的读卡器阻塞任意长时间.
可以改用ArcSwap
,这解决了上述问题,并且在竞争和非竞争场景中都比RwLock
具有更好的性能特征.
我经常主张执行某种更智能的算法.例如,你可以旋转N个线程,每个线程有自己的HashMap
个线程.然后你就可以在他们中间工作了.例如,对于上面的简单示例,可以使用id % N_THREADS
.还有一些复杂的切分方案取决于您的数据.
AsGo在传福音方面做得很好:do not communicate by sharing memory; instead, share memory by communicating人.