面试题答案
一键面试减少锁争用策略
- 缩小锁的粒度:
- 避免使用一个大的Mutex来保护所有数据。例如,在一个包含多个独立数据块的结构体中,为每个数据块单独使用Mutex。
struct Data { part1: Mutex<u32>, part2: Mutex<u32> }
- 这样不同的线程可以同时访问不同的数据块,减少锁争用。
- 缩短锁的持有时间:
- 将不需要锁保护的操作移出锁的作用域。例如:
let mut data = mutex.lock().unwrap(); let value = *data; drop(data); // 提前释放锁 // 这里进行对value的操作,此时锁已经释放
- 读写锁替代:
- 如果读操作远多于写操作,可以使用
RwLock
。读操作可以并发执行,只有写操作需要独占锁。
use std::sync::RwLock; let rw_lock = RwLock::new(0); // 多个读操作可以并发 let reader1 = rw_lock.read().unwrap(); let reader2 = rw_lock.read().unwrap(); // 写操作需要独占锁 let mut writer = rw_lock.write().unwrap();
- 如果读操作远多于写操作,可以使用
结合条件变量优化并发逻辑
- 生产者 - 消费者模型:
- 使用Mutex和条件变量
Condvar
实现生产者 - 消费者模型。例如:
use std::sync::{Arc, Condvar, Mutex}; use std::thread; struct SharedData { data: Vec<i32>, is_full: bool, } fn main() { let shared = Arc::new((Mutex::new(SharedData { data: Vec::new(), is_full: false }), Condvar::new())); let producer_shared = shared.clone(); let consumer_shared = shared.clone(); let producer = thread::spawn(move || { let (lock, cvar) = &*producer_shared; let mut data = lock.lock().unwrap(); for i in 0..10 { while data.is_full { data = cvar.wait(data).unwrap(); } data.data.push(i); if data.data.len() == 5 { data.is_full = true; } cvar.notify_one(); } }); let consumer = thread::spawn(move || { let (lock, cvar) = &*consumer_shared; let mut data = lock.lock().unwrap(); while data.data.len() < 10 { while!data.is_full { data = cvar.wait(data).unwrap(); } let item = data.data.remove(0); if data.data.len() < 5 { data.is_full = false; } cvar.notify_one(); println!("Consumed: {}", item); } }); producer.join().unwrap(); consumer.join().unwrap(); }
- 在这个模型中,生产者和消费者通过Mutex保护共享数据,并使用条件变量来通知对方状态的改变,从而避免不必要的锁等待,优化了并发逻辑。
- 使用Mutex和条件变量
- 信号量模拟:
- 可以用条件变量和Mutex模拟信号量的行为。例如,实现一个简单的信号量来限制同时访问资源的线程数量:
use std::sync::{Arc, Condvar, Mutex}; use std::thread; struct Semaphore { count: i32, max_count: i32, } impl Semaphore { fn new(max_count: i32) -> Self { Semaphore { count: max_count, max_count } } fn acquire(&self, lock: &Mutex<()>) { let mut data = lock.lock().unwrap(); while self.count <= 0 { data = self.condvar.wait(data).unwrap(); } self.count -= 1; } fn release(&self, lock: &Mutex<()>) { let mut data = lock.lock().unwrap(); self.count += 1; if self.count <= self.max_count { self.condvar.notify_one(); } } } fn main() { let semaphore = Arc::new(Semaphore::new(2)); let lock = Arc::new(Mutex::new(())); let semaphore1 = semaphore.clone(); let semaphore2 = semaphore.clone(); let semaphore3 = semaphore.clone(); let thread1 = thread::spawn(move || { semaphore1.acquire(&lock); println!("Thread 1 acquired semaphore"); thread::sleep(std::time::Duration::from_secs(1)); semaphore1.release(&lock); println!("Thread 1 released semaphore"); }); let thread2 = thread::spawn(move || { semaphore2.acquire(&lock); println!("Thread 2 acquired semaphore"); thread::sleep(std::time::Duration::from_secs(1)); semaphore2.release(&lock); println!("Thread 2 released semaphore"); }); let thread3 = thread::spawn(move || { semaphore3.acquire(&lock); println!("Thread 3 acquired semaphore"); thread::sleep(std::time::Duration::from_secs(1)); semaphore3.release(&lock); println!("Thread 3 released semaphore"); }); thread1.join().unwrap(); thread2.join().unwrap(); thread3.join().unwrap(); }
- 通过这种方式,结合Mutex和条件变量,可以更灵活地控制并发访问,优化高并发场景下的性能。