面试题答案
一键面试1. 原子操作对性能的影响
load
操作:- 性能影响:
load
操作从原子变量中读取值。不同的内存顺序对性能有不同影响。例如,Relaxed
内存顺序是最宽松的,它不提供任何跨线程的内存可见性保证,仅保证该操作的原子性。这意味着它的性能开销相对较小,因为不需要与其他线程同步内存状态。而SeqCst
(顺序一致性)内存顺序是最严格的,它保证所有线程都以相同顺序观察到所有原子操作,这会带来较大的性能开销,因为需要与其他线程进行大量的内存同步。 - 示例:
- 性能影响:
use std::sync::atomic::{AtomicUsize, Ordering};
let atomic_var = AtomicUsize::new(0);
let value = atomic_var.load(Ordering::Relaxed);
store
操作:- 性能影响:
store
操作用于向原子变量写入值。同样,不同内存顺序影响性能。Relaxed
顺序下,写入操作可以与其他线程的操作乱序执行,性能开销小。Release
内存顺序在写入时,会确保在该操作之前的所有内存访问都对后续获取(Acquire
)该变量的线程可见,这需要一定的同步开销,性能略低于Relaxed
。 - 示例:
- 性能影响:
use std::sync::atomic::{AtomicUsize, Ordering};
let atomic_var = AtomicUsize::new(0);
atomic_var.store(42, Ordering::Release);
2. 根据场景选择原子操作和内存顺序
- 延迟一次性初始化场景:
- 单线程初始化,多线程读取:如果是在单线程中进行初始化,然后多个线程读取初始化后的值,那么在初始化时可以使用
Release
顺序的store
操作,在读取时使用Acquire
顺序的load
操作。这样可以保证初始化完成后,所有读取线程都能看到正确的初始化值,同时性能开销相对SeqCst
较小。 - 示例:
- 单线程初始化,多线程读取:如果是在单线程中进行初始化,然后多个线程读取初始化后的值,那么在初始化时可以使用
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
static mut INITIALIZED: AtomicBool = AtomicBool::new(false);
static mut VALUE: AtomicUsize = AtomicUsize::new(0);
fn initialize() {
let local_value = 42;
unsafe {
VALUE.store(local_value, Ordering::Release);
INITIALIZED.store(true, Ordering::Release);
}
}
fn get_value() -> usize {
unsafe {
while!INITIALIZED.load(Ordering::Acquire) {
std::thread::yield_now();
}
VALUE.load(Ordering::Acquire)
}
}
- **多线程竞争初始化**:如果存在多个线程可能同时尝试初始化的情况,可以使用`CompareAndSwap`(`compare_exchange`)操作,结合`SeqCst`内存顺序。这可以确保只有一个线程能够成功初始化,并且所有线程都能以一致的顺序观察到初始化操作。虽然`SeqCst`开销大,但在这种需要严格同步的场景下是必要的。
- **示例**:
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
static mut INITIALIZED: AtomicBool = AtomicBool::new(false);
static mut VALUE: AtomicUsize = AtomicUsize::new(0);
fn initialize() {
let local_value = 42;
unsafe {
loop {
if INITIALIZED.load(Ordering::SeqCst) {
break;
}
match INITIALIZED.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(_) => {
VALUE.store(local_value, Ordering::SeqCst);
break;
}
Err(_) => {}
}
}
}
}
fn get_value() -> usize {
unsafe {
while!INITIALIZED.load(Ordering::SeqCst) {
std::thread::yield_now();
}
VALUE.load(Ordering::SeqCst)
}
}
通过根据具体场景选择合适的原子操作和内存顺序,可以在保证程序正确性的前提下优化延迟一次性初始化的性能。