面试题答案
一键面试可能遇到的并发相关问题
- 数据竞争(Data Race):多个线程同时访问和修改ID分配的共享状态,可能导致未定义行为,因为不同线程对内存的读写操作顺序不可预测。
- 竞态条件(Race Condition):当多个线程竞争对ID分配逻辑的控制时,可能导致ID分配出现错误,例如重复分配或跳过某些ID。
利用Rust特性解决问题
- 使用Mutex
- 原理:Mutex(互斥锁)用于保护共享数据,确保同一时间只有一个线程可以访问和修改共享数据,从而避免数据竞争。
- 示例:
use std::sync::{Arc, Mutex};
fn main() {
let id_counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&id_counter);
let handle = std::thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
println!("Thread generated ID: {}", *num);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
- 使用Atomic类型
- 原理:Atomic类型提供原子操作,允许在不使用锁的情况下进行线程安全的读写操作。这可以避免锁带来的性能开销,尤其在高并发场景下。
- 示例:
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
fn main() {
let id_counter = Arc::new(AtomicU64::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&id_counter);
let handle = std::thread::spawn(move || {
let id = counter.fetch_add(1, Ordering::SeqCst);
println!("Thread generated ID: {}", id + 1);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
在上述Atomic类型的示例中,fetch_add
方法是原子操作,它会在获取当前值的同时增加计数器的值,保证了在多线程环境下ID分配的正确性。并且由于是原子操作,无需像Mutex那样通过锁来保护共享数据,提升了并发性能。