MST
星途 面试题库

面试题:Rust中原子操作的基础使用

在Rust中,如何使用原子类型(如`AtomicUsize`)来实现一个简单的计数器?请编写代码展示,并且解释在多线程环境下这样做与普通变量的区别。
22.9万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

代码实现

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

fn main() {
    let counter = AtomicUsize::new(0);

    let mut handles = vec![];
    for _ in 0..10 {
        let counter_clone = counter.clone();
        let handle = thread::spawn(move || {
            for _ in 0..100 {
                counter_clone.fetch_add(1, Ordering::SeqCst);
            }
        });
        handles.push(handle);
    }

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

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

多线程环境下与普通变量的区别

  1. 普通变量:在多线程环境下,如果多个线程同时访问和修改普通变量,会导致数据竞争(data race)问题。Rust的借用检查器在编译时无法检测到这种运行时的数据竞争,因为线程是在运行时创建和调度的。例如,如果两个线程同时读取和修改同一个普通变量,可能会导致其中一个线程的修改丢失,或者读到不一致的数据。
  2. 原子类型:使用原子类型(如AtomicUsize)可以避免数据竞争问题。原子操作是不可分割的,在多线程环境下,对原子类型的操作不会被其他线程打断。fetch_add方法就是一个原子操作,它会在增加计数器值的同时,保证这个操作的原子性,所以多个线程可以安全地对AtomicUsize类型的计数器进行操作。Ordering参数则控制了内存顺序,例如Ordering::SeqCst提供了最强的顺序保证,确保所有线程以相同的顺序看到所有原子操作。