面试题答案
一键面试使用RwLock优化读写性能
- 原理:RwLock(读写锁)允许同一时间有多个线程进行读操作,但只允许一个线程进行写操作。读操作之间不会相互阻塞,提高了并发读的性能。
- 代码示例:
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(String::from("initial data")));
let mut read_handles = vec![];
for _ in 0..3 {
let data_clone = Arc::clone(&data);
let handle = thread::spawn(move || {
let read_data = data_clone.read().unwrap();
println!("Read data: {}", read_data);
});
read_handles.push(handle);
}
let write_handle = thread::spawn(move || {
let mut write_data = data.write().unwrap();
*write_data = String::from("new data");
println!("Write data: {}", write_data);
});
for handle in read_handles {
handle.join().unwrap();
}
write_handle.join().unwrap();
}
在上述代码中,多个读线程可以同时获取读锁读取数据,而写线程获取写锁时会阻塞其他读线程和写线程,直到写操作完成。
可能产生死锁的情况
- 情况描述:当多个线程以不同顺序获取多个RwLock时,可能会产生死锁。例如,线程A获取RwLock1的写锁,然后尝试获取RwLock2的写锁;而线程B获取RwLock2的写锁,然后尝试获取RwLock1的写锁。这样两个线程都会阻塞,形成死锁。
- 代码示例:
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let rwlock1 = Arc::new(RwLock::new(0));
let rwlock2 = Arc::new(RwLock::new(0));
let rwlock1_clone = Arc::clone(&rwlock1);
let rwlock2_clone = Arc::clone(&rwlock2);
let thread1 = thread::spawn(move || {
let mut lock1 = rwlock1_clone.write().unwrap();
println!("Thread 1 acquired lock 1");
let lock2 = rwlock2_clone.write().unwrap();
println!("Thread 1 acquired lock 2");
});
let thread2 = thread::spawn(move || {
let mut lock2 = rwlock2.write().unwrap();
println!("Thread 2 acquired lock 2");
let lock1 = rwlock1.write().unwrap();
println!("Thread 2 acquired lock 1");
});
thread1.join().unwrap();
thread2.join().unwrap();
}
这个示例中,thread1
和thread2
以不同顺序获取rwlock1
和rwlock2
,很可能导致死锁。
避免死锁的方法
- 按固定顺序获取锁:所有线程都按照相同的顺序获取多个锁。例如,在上述代码中,所有线程都先获取
rwlock1
,再获取rwlock2
。 - 使用
try_lock
方法:使用try_lock
方法尝试获取锁,如果获取失败,线程可以选择释放已经获取的锁,并稍后重试。
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let rwlock1 = Arc::new(RwLock::new(0));
let rwlock2 = Arc::new(RwLock::new(0));
let rwlock1_clone = Arc::clone(&rwlock1);
let rwlock2_clone = Arc::clone(&rwlock2);
let thread1 = thread::spawn(move || {
loop {
match (rwlock1_clone.try_write(), rwlock2_clone.try_write()) {
(Ok(mut lock1), Ok(mut lock2)) => {
println!("Thread 1 acquired both locks");
break;
}
_ => {
println!("Thread 1 failed to acquire locks, retrying...");
thread::sleep(std::time::Duration::from_millis(100));
}
}
}
});
let thread2 = thread::spawn(move || {
loop {
match (rwlock1.try_write(), rwlock2.try_write()) {
(Ok(mut lock1), Ok(mut lock2)) => {
println!("Thread 2 acquired both locks");
break;
}
_ => {
println!("Thread 2 failed to acquire locks, retrying...");
thread::sleep(std::time::Duration::from_millis(100));
}
}
}
});
thread1.join().unwrap();
thread2.join().unwrap();
}
在这个改进后的代码中,线程使用try_write
尝试获取锁,如果获取失败则等待一段时间后重试,避免了死锁。