MST

星途 面试题库

面试题:Rust原子操作在多线程环境下的复杂应用

在一个多线程环境中,有多个线程需要对一个`AtomicU64`类型的变量进行操作。每个线程先获取当前值,然后根据当前值计算一个新值并修改回去(假设新值为当前值的平方)。请设计并实现这段代码,同时要考虑如何处理可能出现的竞争条件,并且说明`Ordering`参数在这个场景中的作用。
45.5万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;

fn main() {
    let num_threads = 10;
    let shared_value = AtomicU64::new(1);

    let mut handles = Vec::with_capacity(num_threads);
    for _ in 0..num_threads {
        let shared_value_clone = shared_value.clone();
        let handle = thread::spawn(move || {
            loop {
                let current = shared_value_clone.load(Ordering::SeqCst);
                let new_value = current * current;
                if shared_value_clone.compare_and_swap(current, new_value, Ordering::SeqCst) == current {
                    break;
                }
            }
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final value: {}", shared_value.load(Ordering::SeqCst));
}

Ordering参数的作用

  1. SeqCst(顺序一致性)
    • 在这个场景中使用SeqCst,保证了所有线程对AtomicU64变量的操作顺序是一致的。也就是说,所有线程都能以相同的顺序观察到对这个变量的修改。
    • 虽然SeqCst是最严格的内存序,它会带来一定的性能开销,但能确保所有线程看到的内存状态变化是一致的,适合在对数据一致性要求极高的场景中使用。
  2. 其他可能的Ordering
    • AcquireReleaseAcquire内存序保证了从内存加载数据时,在此之前的所有读操作都已完成。Release内存序保证了在此之后的所有写操作都已完成。如果使用AcquireRelease内存序来替代SeqCst,在这个场景中可能会导致一些线程看到不一致的操作顺序,因为它们不如SeqCst严格。
    • RelaxedRelaxed内存序是最宽松的,它只保证原子操作本身的原子性,不保证任何内存顺序。在这个场景中如果使用Relaxed,可能会出现线程读取到旧值或者写入的值没有及时被其他线程看到的情况,无法满足需求。

通过使用合适的Ordering,特别是SeqCst,在这个多线程操作AtomicU64变量的场景中,能够有效地处理竞争条件,保证数据的一致性。