面试题答案
一键面试-
引入必要的库:
use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::thread;
-
定义共享资源:
let shared_map: Arc<Mutex<HashMap<String, i32>>> = Arc::new(Mutex::new(HashMap::new()));
这里使用
Arc
(原子引用计数)来实现跨线程的共享所有权,Mutex
(互斥锁)用于保护HashMap
,确保同一时间只有一个线程可以访问它。 -
创建线程并操作共享资源:
let mut handles = vec![]; for _ in 0..10 { let map_clone = shared_map.clone(); let handle = thread::spawn(move || { let mut map = map_clone.lock().unwrap(); map.insert(String::from("key"), 42); let value = map.get(&String::from("key")).cloned(); println!("Thread got value: {:?}", value); }); handles.push(handle); }
- 首先通过
shared_map.clone()
克隆Arc
,这不会增加HashMap
的拷贝,只是增加引用计数。 - 使用
move
闭包将map_clone
所有权转移到新线程中。 - 在线程内部,通过
map_clone.lock().unwrap()
获取MutexGuard
,这个过程会尝试获取锁。如果锁可用,会返回一个MutexGuard
,它实现了Deref
和DerefMut
,可以像直接操作HashMap
一样操作被保护的数据。这里的MutexGuard
拥有对HashMap
的临时独占访问权,保证了数据一致性,避免竞态条件。 - 当
MutexGuard
离开作用域(例如线程结束),它会自动释放锁,其他线程就可以获取锁并访问HashMap
。
- 首先通过
-
等待所有线程结束:
for handle in handles { handle.join().unwrap(); }
所有权机制在多线程中的管理和转移
- 所有权转移:在Rust中,所有权是非常严格的概念。通过
Arc::clone
只是克隆了引用计数指针,实际的数据只有一份。当使用move
闭包将Arc
转移到线程中时,所有权从主线程转移到了新线程。但由于Arc
的引用计数特性,只要还有其他Arc
实例存在(在这个例子中主线程的shared_map
),数据就不会被释放。 - 数据一致性和竞态条件避免:
Mutex
通过独占访问的方式来确保数据一致性。当一个线程获取到MutexGuard
时,其他线程无法访问被保护的数据,从而避免了竞态条件。MutexGuard
的生命周期决定了锁的持有时间,当MutexGuard
离开作用域,锁会自动释放,保证了锁的正确管理。这种方式结合所有权机制,使得在多线程环境下共享资源的访问既安全又高效。