面试题答案
一键面试use std::sync::{Arc, Once};
use std::sync::atomic::{AtomicBool, Ordering};
static INITIALIZED: AtomicBool = AtomicBool::new(false);
static mut VALUE: Option<i32> = None;
fn get_value() -> i32 {
if INITIALIZED.load(Ordering::SeqCst) {
unsafe { VALUE.unwrap() }
} else {
let local_value = expensive_computation();
INITIALIZED.store(true, Ordering::SeqCst);
unsafe {
VALUE = Some(local_value);
}
local_value
}
}
fn expensive_computation() -> i32 {
// 这里模拟一个耗时的计算
42
}
fn main() {
let result = get_value();
println!("The value is: {}", result);
}
原子操作的作用
- 线程安全的初始化状态跟踪:在多线程环境下,
AtomicBool
类型的INITIALIZED
原子变量用于跟踪变量是否已经初始化。原子操作保证了不同线程对这个变量的读写操作是原子的,避免了数据竞争。例如,如果多个线程同时检查INITIALIZED
是否为false
并尝试初始化,原子操作能确保只有一个线程能成功修改INITIALIZED
为true
,从而保证初始化的唯一性。 - 内存可见性:通过使用合适的内存序(如
Ordering::SeqCst
),原子操作确保了在一个线程中对INITIALIZED
的修改对其他线程是可见的。这意味着一旦一个线程设置INITIALIZED
为true
,其他线程读取INITIALIZED
时能看到这个变化,进而直接获取已经初始化的值,而不会再次进行初始化。
另外,使用 std::sync::Once
也可以更方便地实现一次性初始化:
use std::sync::{Arc, Once};
static INIT: Once = Once::new();
static mut VALUE: i32 = 0;
fn get_value() -> i32 {
INIT.call_once(|| {
unsafe {
VALUE = expensive_computation();
}
});
unsafe { VALUE }
}
fn expensive_computation() -> i32 {
42
}
fn main() {
let result = get_value();
println!("The value is: {}", result);
}
Once
类型内部也是通过原子操作来保证初始化的唯一性和线程安全,它提供了更简洁的 API 来实现延迟一次性初始化。