MST

星途 面试题库

面试题:Rust内部可变性:复杂场景应用

假设有一个`Rust`程序,需要在多线程环境下安全地共享一个可变状态。你已经定义了一个结构体`SharedData`,它包含一些数据成员。现在要求使用`Rust`的内部可变性机制(如`Cell`或`RefCell`)以及线程安全相关的工具(如`Mutex`等),实现不同线程能够安全地读取和修改`SharedData`中的数据。请写出完整的代码实现,并解释每一步的作用和原理。
17.3万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试
use std::sync::{Arc, Mutex};
use std::cell::RefCell;

// 定义包含数据成员的结构体
struct SharedData {
    value: i32,
}

fn main() {
    // 使用Arc来实现数据的共享,Mutex用于线程安全地访问数据
    let shared_data = Arc::new(Mutex::new(RefCell::new(SharedData { value: 0 })));

    let mut handles = vec![];
    for _ in 0..10 {
        // 克隆Arc,以便每个线程都有自己的引用
        let data = shared_data.clone();
        let handle = std::thread::spawn(move || {
            // 锁定Mutex,获取对RefCell的可变引用
            let mut inner = data.lock().unwrap();
            let mut shared = inner.borrow_mut();
            // 修改数据
            shared.value += 1;
            // 打印修改后的值
            println!("Thread modified value: {}", shared.value);
        });
        handles.push(handle);
    }

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

    // 主线程获取并打印最终的值
    let final_value = Arc::try_unwrap(shared_data)
      .ok()
      .unwrap()
      .into_inner()
      .unwrap()
      .borrow()
      .value;
    println!("Final value: {}", final_value);
}

代码解释

  1. 结构体定义
    • struct SharedData 定义了包含一个 i32 类型数据成员 value 的结构体,这是我们要共享和修改的数据。
  2. 共享与线程安全工具
    • Arc<Mutex<RefCell<SharedData>>>
      • Arc(原子引用计数)用于在多个线程间安全地共享数据,因为 Arc 内部使用原子操作来管理引用计数,所以是线程安全的。
      • Mutex(互斥锁)用于确保同一时间只有一个线程能够访问其内部的数据。当一个线程调用 lock 方法时,如果锁可用,线程会获得锁并可以访问数据;如果锁已被其他线程持有,该线程会被阻塞直到锁被释放。
      • RefCell 提供内部可变性,允许在不可变引用的情况下修改其内部数据。这在 Rust 中通常用于在运行时检查借用规则,而不是编译时。因为 Mutex 本身是不可变的,所以需要 RefCell 来允许修改内部的 SharedData
  3. 多线程操作
    • for 循环中,通过 Arc::clone 为每个线程创建 shared_data 的克隆,这样每个线程都持有对共享数据的引用。
    • 每个线程通过 data.lock().unwrap() 来获取 Mutex 的锁,如果获取成功(unwrap 方法在获取失败时会导致程序 panic),就可以访问内部的 RefCell
    • 接着通过 inner.borrow_mut() 获取对 SharedData 的可变引用,这样就可以修改 value 成员。
  4. 主线程等待及获取最终值
    • for handle in handles 循环等待所有线程完成。
    • 最后,主线程通过 Arc::try_unwrap 尝试获取唯一的 Arc 实例(如果当前 Arc 引用计数为1),然后依次获取 Mutex 内部的 RefCell,再获取 SharedData 的不可变引用,从而打印最终的 value 值。