MST

星途 面试题库

面试题:Rust中如何在多线程环境下安全使用Vec和LinkedList

假设你正在开发一个多线程Rust程序,需要在不同线程间共享Vec和LinkedList,描述你将如何确保数据的安全访问和避免数据竞争,分别给出针对Vec和LinkedList的具体实现思路和可能用到的同步原语。
19.9万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

针对 Vec

  1. 实现思路
    • Vec 封装在一个可跨线程共享的数据结构中,确保对其的访问是线程安全的。
    • 在线程间传递对这个共享数据结构的引用,并使用同步机制来控制对 Vec 的访问。
  2. 可能用到的同步原语
    • Mutex:使用 std::sync::Mutex 来包裹 VecMutex 提供了互斥锁的功能,同一时间只有一个线程可以获取锁并访问 Vec,从而避免数据竞争。例如:
use std::sync::{Mutex, Arc};
use std::thread;

fn main() {
    let shared_vec = Arc::new(Mutex::new(vec![1, 2, 3]));
    let mut handles = vec![];
    for _ in 0..10 {
        let shared_vec_clone = shared_vec.clone();
        let handle = thread::spawn(move || {
            let mut vec = shared_vec_clone.lock().unwrap();
            vec.push(4);
            println!("{:?}", vec);
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
}
  • RwLock:如果读操作远多于写操作,可以考虑使用 std::sync::RwLock。它允许多个线程同时进行读操作,但只允许一个线程进行写操作。写操作会独占锁,阻止其他读写操作。例如:
use std::sync::{RwLock, Arc};
use std::thread;

fn main() {
    let shared_vec = Arc::new(RwLock::new(vec![1, 2, 3]));
    let mut handles = vec![];
    for _ in 0..5 {
        let shared_vec_clone = shared_vec.clone();
        let handle = thread::spawn(move || {
            let vec = shared_vec_clone.read().unwrap();
            println!("{:?}", vec);
        });
        handles.push(handle);
    }
    for _ in 0..2 {
        let shared_vec_clone = shared_vec.clone();
        let handle = thread::spawn(move || {
            let mut vec = shared_vec_clone.write().unwrap();
            vec.push(4);
            println!("{:?}", vec);
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
}

针对 LinkedList

  1. 实现思路
    • Vec 类似,将 LinkedList 封装在一个线程安全的数据结构中。由于 LinkedList 的节点在内存中不连续,在多线程环境下访问时要特别注意节点的修改和删除操作,防止出现悬空指针等问题。
  2. 可能用到的同步原语
    • Mutex:同样可以使用 std::sync::Mutex 包裹 LinkedList。通过获取锁来保证同一时间只有一个线程能访问和修改 LinkedList。例如:
use std::sync::{Mutex, Arc};
use std::collections::LinkedList;
use std::thread;

fn main() {
    let shared_list = Arc::new(Mutex::new(LinkedList::from(vec![1, 2, 3])));
    let mut handles = vec![];
    for _ in 0..10 {
        let shared_list_clone = shared_list.clone();
        let handle = thread::spawn(move || {
            let mut list = shared_list_clone.lock().unwrap();
            list.push_back(4);
            println!("{:?}", list);
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
}
  • RwLock:当读多写少场景时,使用 std::sync::RwLock 包裹 LinkedList,读操作可以并发执行,写操作则独占锁。例如:
use std::sync::{RwLock, Arc};
use std::collections::LinkedList;
use std::thread;

fn main() {
    let shared_list = Arc::new(RwLock::new(LinkedList::from(vec![1, 2, 3])));
    let mut handles = vec![];
    for _ in 0..5 {
        let shared_list_clone = shared_list.clone();
        let handle = thread::spawn(move || {
            let list = shared_list_clone.read().unwrap();
            println!("{:?}", list);
        });
        handles.push(handle);
    }
    for _ in 0..2 {
        let shared_list_clone = shared_list.clone();
        let handle = thread::spawn(move || {
            let mut list = shared_list_clone.write().unwrap();
            list.push_back(4);
            println!("{:?}", list);
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
}

此外,对于更复杂的场景,还可以考虑使用 Atomic 类型结合 MutexRwLock 来进一步优化性能和保证数据一致性。