MST

星途 面试题库

面试题:Rust内存模型下的并发编程与数据竞争

假设你正在使用Rust编写一个多线程程序,多个线程需要访问并修改同一个共享数据(比如一个 `Vec<i32>`)。在Rust内存模型下,如何确保线程安全,避免数据竞争?请描述你会采取的措施,并结合Rust的所有权、借用和生命周期概念解释其原理。同时,使用 `std::sync` 相关的工具编写一个简单示例来展示你的解决方案。
30.6万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试
  1. 采取的措施
    • 使用 std::sync::Mutexstd::sync::RwLock 来保护共享数据。Mutex 提供互斥访问,一次只有一个线程可以获取锁并访问共享数据,从而避免数据竞争。RwLock 允许多个线程同时进行读操作,但写操作时会独占锁,也能防止数据竞争。
    • 结合 Arc(原子引用计数)来在多个线程间共享数据。Arc 用于在堆上分配数据,并允许多个线程持有对该数据的引用。
  2. 原理解释
    • 所有权:Rust的所有权系统确保每个值都有一个唯一的所有者。当我们使用 Arc 来共享数据时,Arc 拥有数据的所有权,并且通过引用计数来跟踪有多少个线程持有对数据的引用。当引用计数降为0时,数据被释放。
    • 借用MutexRwLock 通过借用的方式来访问共享数据。当一个线程获取 Mutex 的锁时,它实际上是借用了 Mutex 内部的数据。因为 Mutex 一次只允许一个线程获取锁,所以不会出现多个线程同时可变借用(写操作)或同时存在可变借用和不可变借用(读写冲突)的情况,这符合Rust借用规则,从而避免数据竞争。
    • 生命周期ArcMutex 所管理的数据的生命周期由 Arc 的引用计数决定。只要有 Arc 实例持有数据的引用,数据就会一直存活。
  3. 示例代码
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let shared_data = Arc::new(Mutex::new(Vec::new()));
    let mut handles = Vec::new();

    for _ in 0..10 {
        let data = Arc::clone(&shared_data);
        let handle = thread::spawn(move || {
            let mut numbers = data.lock().unwrap();
            numbers.push(1);
        });
        handles.push(handle);
    }

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

    let result = shared_data.lock().unwrap();
    println!("Final result: {:?}", result);
}

在这个示例中,Arc<Mutex<Vec<i32>>> 用于在多个线程间共享 Vec<i32>。每个线程通过获取 Mutex 的锁来可变借用 Vec<i32> 并向其添加数据,从而确保了线程安全。