MST

星途 面试题库

面试题:Rust原子操作内存顺序在多线程场景下的应用

假设你正在编写一个多线程Rust程序,其中一个线程负责递增一个共享的`AtomicU64`变量,另一个线程负责读取这个变量。为了确保读取线程能准确获取到递增后的值,应该如何选择合适的原子操作和内存顺序?请详细说明理由,并给出代码示例。如果在这个场景中,错误地使用了过于宽松的内存顺序,可能会出现什么问题?
12.2万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

原子操作和内存顺序的选择及理由

在Rust中,对于AtomicU64类型,要确保读取线程能准确获取到递增后的值,递增线程可以使用fetch_add方法,并搭配Release内存顺序,读取线程使用load方法并搭配Acquire内存顺序。

Release内存顺序保证在这个原子操作之前的所有写操作对其他持有AcquireSeqCst内存顺序的线程可见。Acquire内存顺序保证在这个原子操作之后的所有读操作能看到之前被标记为ReleaseSeqCst内存顺序的写操作。

代码示例

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

fn main() {
    let counter = Arc::new(AtomicU64::new(0));
    let counter_clone = counter.clone();

    let increment_thread = thread::spawn(move || {
        for _ in 0..1000 {
            counter_clone.fetch_add(1, Ordering::Release);
        }
    });

    let read_thread = thread::spawn(move || {
        for _ in 0..1000 {
            let value = counter.load(Ordering::Acquire);
            println!("Read value: {}", value);
        }
    });

    increment_thread.join().unwrap();
    read_thread.join().unwrap();
}

错误使用宽松内存顺序的问题

如果使用过于宽松的内存顺序,比如在递增线程中使用Relaxed内存顺序,可能会导致读取线程看不到递增线程所做的更新。这是因为Relaxed内存顺序不提供任何跨线程的内存可见性保证,它只保证原子操作本身的原子性,而不保证操作的顺序与其他线程的操作顺序之间的关系。所以读取线程可能会读取到旧的值,而不是递增后的值。