面试题答案
一键面试面临的挑战
- 数据竞争(Data Race):多个线程同时读写共享数据时,若没有适当的同步机制,会导致数据竞争,违反Rust的内存安全规则。例如,线程A读取数据,线程B同时写入数据,可能造成未定义行为。
- 借用规则冲突:Rust的借用规则要求在同一时间要么只能有一个可变引用(写操作),要么可以有多个不可变引用(读操作),但在并发环境下,多个线程尝试获取引用时可能会违反此规则。比如,一个线程持有可变引用时,另一个线程尝试获取不可变引用。
设计思路
- 使用
Arc
和Mutex
:Arc
(原子引用计数)用于在多个线程间共享数据,Mutex
(互斥锁)用于保证同一时间只有一个线程可以访问数据。这样可以通过锁机制来避免数据竞争和借用规则冲突。 - 不可变共享:对于只读操作,线程获取
Mutex
的锁后,可以安全地读取数据,因为Mutex
保证了同一时间只有一个线程在访问数据,从而满足Rust的不可变借用规则。 - 可变共享:如果需要进行写操作,线程获取
Mutex
的锁后,进行数据修改,同样满足Rust的可变借用规则。
关键代码片段
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let shared_data = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let data = Arc::clone(&shared_data);
let handle = thread::spawn(move || {
let mut num = data.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Final value: {}", *shared_data.lock().unwrap());
}
在上述代码中:
Arc::new(Mutex::new(0))
创建了一个在多个线程间共享的Mutex
包裹的整数。thread::spawn
创建新线程,每个线程获取Arc
的克隆,并尝试获取Mutex
的锁,获取成功后(lock().unwrap()
)对数据进行修改。- 最后主线程等待所有子线程完成,输出最终结果。这样就通过
Arc
和Mutex
实现了安全且高效的并发访问和修改共享数据。如果只是比较操作,同样获取锁后读取数据进行比较即可。例如:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let shared_data = Arc::new(Mutex::new(5));
let mut handles = vec![];
for _ in 0..10 {
let data = Arc::clone(&shared_data);
let handle = thread::spawn(move || {
let num = data.lock().unwrap();
if *num > 3 {
println!("Data is greater than 3");
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
这里每个线程获取锁后读取数据并进行比较,保证了并发比较的安全性。