MST
星途 面试题库

面试题:Rust中原子加载操作的常用场景及示例

请举例说明在Rust中原子加载操作通常会在哪些场景下使用,并给出对应的代码示例,同时简要阐述原子加载操作在此场景下的作用。
35.2万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

场景一:多线程间共享数据的安全读取

在多线程编程中,当多个线程需要读取共享数据,且该数据可能会被其他线程修改时,为了保证读取到的数据的一致性和完整性,需要使用原子加载操作。

代码示例

use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;

fn main() {
    let shared_data = Arc::new(AtomicUsize::new(0));
    let shared_data_clone = shared_data.clone();

    let handle = thread::spawn(move || {
        // 模拟其他线程修改数据
        shared_data_clone.store(42, Ordering::SeqCst);
    });

    // 主线程读取数据
    let value = shared_data.load(Ordering::SeqCst);
    println!("Read value: {}", value);

    handle.join().unwrap();
}

作用阐述

原子加载操作确保在读取共享数据时,不会读取到部分修改的数据。在这个例子中,Ordering::SeqCst 提供了最强的内存顺序保证,确保读取操作看到的是所有线程对该原子变量的修改的一致视图,避免了数据竞争导致的未定义行为。

场景二:实现无锁数据结构

在构建无锁数据结构(如无锁队列、无锁栈等)时,原子加载操作用于安全地获取数据结构内部状态,同时避免使用锁带来的性能开销。

代码示例

use std::sync::atomic::{AtomicUsize, Ordering};

struct LockFreeStack {
    top: AtomicUsize,
}

impl LockFreeStack {
    fn new() -> Self {
        LockFreeStack { top: AtomicUsize::new(0) }
    }

    fn push(&self, value: usize) {
        // 简化实现,这里仅示意原子操作的使用
        let old_top = self.top.load(Ordering::Relaxed);
        // 这里应实际更新栈的逻辑,假设这里简单加一模拟入栈
        self.top.store(old_top + 1, Ordering::Release);
    }

    fn pop(&self) -> Option<usize> {
        let old_top = self.top.load(Ordering::Acquire);
        if old_top == 0 {
            None
        } else {
            // 假设这里简单减一模拟出栈
            self.top.store(old_top - 1, Ordering::Release);
            Some(old_top - 1)
        }
    }
}

作用阐述

在无锁数据结构中,原子加载操作与原子存储操作配合,确保对数据结构状态的读取和修改是原子性的,不同线程之间可以安全地访问和修改数据结构,而无需使用锁,提高了并发性能。