面试题答案
一键面试1. 原子类型
原子类型用于对单个值进行无锁的原子操作,适用于简单数据类型的共享访问。例如,std::sync::atomic::AtomicUsize
。
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main() {
let counter = AtomicUsize::new(0);
let handles: Vec<_> = (0..10).map(|_| {
let counter = &counter;
thread::spawn(move || {
for _ in 0..100 {
counter.fetch_add(1, Ordering::Relaxed);
}
})
}).collect();
for handle in handles {
handle.join().unwrap();
}
println!("Final counter value: {}", counter.load(Ordering::Relaxed));
}
在这个例子中,AtomicUsize
允许多个线程安全地增加计数器的值,Ordering
用于指定内存访问顺序。
2. 线程安全的数据结构
Mutex
:互斥锁,用于保护共享数据,同一时间只有一个线程可以访问。
use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(vec![1, 2, 3]));
let mut handles = Vec::new();
for _ in 0..10 {
let data = Arc::clone(&data);
let handle = thread::spawn(move || {
let mut data = data.lock().unwrap();
data.push(4);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Final data: {:?}", data.lock().unwrap());
}
这里,Mutex
保护了 Vec
,线程通过 lock
方法获取锁来访问数据。
RwLock
:读写锁,允许多个线程同时读,但只允许一个线程写。适用于读多写少的场景。
use std::sync::{RwLock, Arc};
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(vec![1, 2, 3]));
let mut handles = Vec::new();
for _ in 0..5 {
let data = Arc::clone(&data);
let handle = thread::spawn(move || {
let read_data = data.read().unwrap();
println!("Read data: {:?}", read_data);
});
handles.push(handle);
}
for _ in 0..2 {
let data = Arc::clone(&data);
let handle = thread::spawn(move || {
let mut write_data = data.write().unwrap();
write_data.push(4);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Final data: {:?}", data.read().unwrap());
}
3. 同步原语
Condvar
:条件变量,用于线程间的通知和等待。
use std::sync::{Mutex, Condvar};
use std::thread;
fn main() {
let pair = (Mutex::new(false), Condvar::new());
let (lock, cvar) = &pair;
let handle = thread::spawn(move || {
let mut started = lock.lock().unwrap();
*started = true;
cvar.notify_one();
});
let mut started = lock.lock().unwrap();
while!*started {
started = cvar.wait(started).unwrap();
}
handle.join().unwrap();
}
在这个例子中,一个线程等待条件变量的通知,另一个线程改变条件并通知等待的线程。
4. 所有权与借用规则
遵循Rust的所有权和借用规则,确保在编译期捕获大部分数据竞争问题。例如,避免在多个线程间共享可变引用。
通过合理使用原子类型、线程安全的数据结构和同步原语,并严格遵循Rust的所有权和借用规则,可以有效地避免数据竞争,同时兼顾性能和代码的可维护性。