面试题答案
一键面试Mutex工作原理
Mutex(互斥锁)是一种最基本的同步原语,它基于“互斥访问”的原则。当一个线程获取了Mutex的锁,其他线程就必须等待,直到该线程释放锁。这确保了同一时间只有一个线程可以访问被Mutex保护的数据,从而避免数据竞争。
RwLock工作原理
RwLock(读写锁)允许多个线程同时进行读操作,因为读操作不会修改数据,所以不会产生数据竞争。但是,当有一个线程进行写操作时,其他读线程和写线程都必须等待,直到写操作完成并释放锁。这样可以保证在写操作时数据的一致性。
优先选择Mutex的场景
- 频繁写操作场景:如果在并发环境中,对共享数据的写操作远远多于读操作,那么使用Mutex更合适。因为RwLock在写操作时会阻塞所有读操作,而Mutex直接限制同一时间只有一个线程能访问数据,无论读写。
- 数据一致性要求高且操作复杂场景:当共享数据的操作涉及到复杂的逻辑,需要保证严格的数据一致性,使用Mutex可以简化逻辑,因为同一时间只有一个线程能操作数据。
优先选择RwLock的场景
- 多读少写场景:当对共享数据的读操作远远多于写操作时,RwLock能显著提高并发性能。因为多个读线程可以同时访问数据,而只有写操作时才会阻塞其他线程。
实际代码中使用Mutex以避免死锁
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(0));
let data_clone = data.clone();
let handle = thread::spawn(move || {
let mut num = data_clone.lock().unwrap();
*num += 1;
});
let mut num = data.lock().unwrap();
*num += 1;
handle.join().unwrap();
println!("Final value: {}", *num);
}
在这段代码中,通过Arc
和Mutex
来保护共享数据data
。每个线程在访问数据前先获取锁lock()
,操作完成后自动释放锁(MutexGuard
离开作用域时),避免死锁。
实际代码中使用RwLock以避免死锁
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(0));
let data_clone = data.clone();
let read_handle = thread::spawn(move || {
let num = data_clone.read().unwrap();
println!("Read value: {}", *num);
});
let write_handle = thread::spawn(move || {
let mut num = data.write().unwrap();
*num += 1;
});
read_handle.join().unwrap();
write_handle.join().unwrap();
let num = data.read().unwrap();
println!("Final value: {}", *num);
}
这里使用RwLock
来保护共享数据data
。读线程通过read()
获取读锁,写线程通过write()
获取写锁。获取锁后同样会在MutexGuard
离开作用域时自动释放锁,避免死锁。