MST

星途 面试题库

面试题:Rust 中如何利用原子操作实现简单的延迟一次性初始化

请用 Rust 代码示例展示如何使用原子操作来实现一个延迟一次性初始化的变量。要求在多线程环境下也能保证初始化的唯一性,简要说明原子操作在此场景中的作用。
10.5万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试
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);
}

原子操作的作用

  1. 线程安全的初始化状态跟踪:在多线程环境下,AtomicBool 类型的 INITIALIZED 原子变量用于跟踪变量是否已经初始化。原子操作保证了不同线程对这个变量的读写操作是原子的,避免了数据竞争。例如,如果多个线程同时检查 INITIALIZED 是否为 false 并尝试初始化,原子操作能确保只有一个线程能成功修改 INITIALIZEDtrue,从而保证初始化的唯一性。
  2. 内存可见性:通过使用合适的内存序(如 Ordering::SeqCst),原子操作确保了在一个线程中对 INITIALIZED 的修改对其他线程是可见的。这意味着一旦一个线程设置 INITIALIZEDtrue,其他线程读取 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 来实现延迟一次性初始化。