面试题答案
一键面试可变变量在Rust并发编程中的挑战
- 数据竞争:多个线程同时读写可变变量,可能导致数据不一致,这违反了Rust的内存安全原则。
- 竞态条件:由于线程调度的不确定性,可变变量的读写顺序可能导致程序出现意外行为。
使用同步原语安全共享可变数据
- Mutex(互斥锁):通过互斥访问机制,保证同一时间只有一个线程可以访问共享数据,从而避免数据竞争。
- Arc(原子引用计数):用于在多个线程间共享数据,其内部实现了原子操作来保证引用计数的线程安全。结合Mutex,可以实现线程安全的共享可变数据。
代码示例
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// 创建一个Arc<Mutex<T>>类型的共享可变数据
let shared_data = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
// 克隆Arc,以便每个线程都有自己的引用
let data = Arc::clone(&shared_data);
let handle = thread::spawn(move || {
// 锁定Mutex以获取对共享数据的可变引用
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());
}
代码解释
-
创建共享数据:
let shared_data = Arc::new(Mutex::new(0));
Arc::new
创建一个引用计数指针,用于线程间共享数据。Mutex::new(0)
创建一个互斥锁,内部包装了初始值为0的可变数据。
-
线程创建:
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); }
Arc::clone
克隆Arc,使得每个线程都有自己对共享数据的引用,引用计数增加。thread::spawn
创建新线程,并将共享数据的克隆传递进去。data.lock().unwrap()
获取Mutex的锁,返回一个MutexGuard
,这是一个RAII(Resource Acquisition Is Initialization)类型,在其生命周期结束时自动释放锁。*num += 1;
对共享数据进行修改。
-
等待所有线程完成:
for handle in handles { handle.join().unwrap(); }
handle.join()
等待每个线程完成,确保所有线程对共享数据的操作都已执行完毕。
-
打印最终结果:
println!("Final value: {}", *shared_data.lock().unwrap());
- 再次锁定Mutex获取对共享数据的可变引用,打印最终结果。整个过程通过Mutex保证了同一时间只有一个线程可以访问和修改共享数据,从而确保数据一致性并避免数据竞争。