面试题答案
一键面试使用RwLock
实现线程安全的HashMap
读写
在Rust中,RwLock
(读写锁)适合这种场景,允许多个线程同时读,但只允许一个线程写。
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let shared_map: Arc<RwLock<HashMap<String, i32>>> = Arc::new(RwLock::new(HashMap::new()));
let mut handles = vec![];
for _ in 0..10 {
let map_clone = shared_map.clone();
let handle = thread::spawn(move || {
let mut map = map_clone.write().unwrap();
map.insert(String::from("key"), 42);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let read_handle = thread::spawn(move || {
let map = shared_map.read().unwrap();
println!("Value: {:?}", map.get("key"));
});
read_handle.join().unwrap();
}
代码分析
- 创建共享的
HashMap
:使用Arc
(原子引用计数)和RwLock
包装HashMap
,使其可以在多个线程间安全共享。Arc
用于在多个线程间共享所有权,RwLock
提供读写锁机制。 - 写操作:每个写线程通过
map_clone.write().unwrap()
获取写锁。如果锁已被占用,线程将阻塞直到锁可用。写锁获取成功后,可以安全地修改HashMap
。 - 读操作:读线程通过
map.read().unwrap()
获取读锁。多个读线程可以同时获取读锁,因为读操作不会修改数据,所以不会产生数据竞争。
性能瓶颈分析
- 写操作阻塞:写操作需要独占锁,当有写操作进行时,其他读写操作都将被阻塞。如果写操作频繁,会导致读操作等待,降低系统的并发性能。
- 锁争用:在高并发场景下,多个线程频繁竞争读写锁,可能导致大量线程处于等待状态,增加上下文切换开销,降低系统整体性能。
- 死锁风险:如果在获取锁的过程中出现错误的逻辑,例如在持有锁的情况下再次尝试获取相同的锁,可能导致死锁。虽然Rust通过类型系统和所有权规则减少了死锁的可能性,但仍然需要小心编写代码。