MST

星途 面试题库

面试题:Rust多线程复杂场景下happens - before关系的分析

考虑如下Rust多线程场景:有三个线程T1、T2、T3,T1初始化一个共享的`Arc<Mutex<Vec<i32>>>`,并向其中添加元素;T2从这个共享的`Vec<i32>`中删除元素;T3对共享的`Vec<i32>`进行排序操作。假设T1必须在T2和T3之前完成初始化添加操作,T2和T3可以并行执行,但T2的删除操作要在T3的排序操作之前(从逻辑数据一致性角度)。请设计一个合理的同步机制,用Rust代码实现,并详细说明在这个实现中是如何保证各种happens - before关系的。
45.3万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试
use std::sync::{Arc, Mutex, Condvar, mpsc};
use std::thread;

fn main() {
    let data = Arc::new(Mutex::new(Vec::new()));
    let cond = Arc::new(Condvar::new());
    let (tx, rx) = mpsc::channel();

    // T1线程
    let t1 = thread::spawn({
        let data = data.clone();
        let cond = cond.clone();
        let tx = tx.clone();
        move || {
            let mut data = data.lock().unwrap();
            data.push(1);
            data.push(2);
            data.push(3);
            println!("T1: 初始化添加操作完成");
            // 通知其他线程初始化完成
            cond.notify_all();
            // 发送信号给T2和T3可以开始执行
            tx.send(()).unwrap();
        }
    });

    // T2线程
    let t2 = thread::spawn({
        let data = data.clone();
        let cond = cond.clone();
        let rx = rx.clone();
        move || {
            let mut data = data.lock().unwrap();
            // 等待T1初始化完成
            data = cond.wait(data).unwrap();
            // 等待T1发送的开始信号
            rx.recv().unwrap();
            if let Some(_) = data.pop() {
                println!("T2: 删除操作完成");
            }
        }
    });

    // T3线程
    let t3 = thread::spawn({
        let data = data.clone();
        let cond = cond.clone();
        let rx = rx.clone();
        move || {
            let mut data = data.lock().unwrap();
            // 等待T1初始化完成
            data = cond.wait(data).unwrap();
            // 等待T1发送的开始信号
            rx.recv().unwrap();
            data.sort();
            println!("T3: 排序操作完成");
        }
    });

    t1.join().unwrap();
    t2.join().unwrap();
    t3.join().unwrap();
}

happens - before关系保证说明:

  1. T1在T2和T3之前完成初始化添加操作
    • 使用Condvar来实现。T2和T3在获取Mutex锁后,调用cond.wait进入等待状态,直到T1调用cond.notify_all通知它们。这确保了T1的初始化添加操作在T2和T3的操作之前完成,因为wait会释放锁并阻塞线程,直到被通知。这里cond.waitcond.notify_all之间存在一种隐式的同步关系,即notify_all之前的所有操作(T1的添加操作)都在wait返回后的操作(T2和T3的操作)之前发生。
  2. T2的删除操作在T3的排序操作之前
    • 通过mpsc通道来实现。T1在完成初始化添加操作后,通过tx.send(())发送一个信号。T2和T3都通过rx.recv()接收这个信号,因为通道是先进先出的,所以T2会先接收到信号并执行删除操作,然后T3才会接收到信号执行排序操作。这种基于通道的同步机制保证了T2的删除操作在T3的排序操作之前从逻辑数据一致性角度发生。

以上代码通过Condvarmpsc通道实现了所需的线程同步和happens - before关系。