MST

星途 面试题库

面试题:Rust线程同步函数调用基础

在Rust中,简述`std::sync::Mutex`和`std::sync::RwLock`在线程同步函数调用场景下的主要区别,并举例说明何时应优先选择`RwLock`。
22.8万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

主要区别

  1. 读写特性
    • std::sync::Mutex只允许同一时间有一个线程访问其内部数据,无论是读操作还是写操作。这意味着每次访问都需要获取独占锁,读写操作相互阻塞。
    • std::sync::RwLock允许多个线程同时进行读操作,因为读操作不会修改数据,不会产生数据竞争。但是写操作需要独占锁,当有线程进行写操作时,其他读写线程都要等待。写操作会阻塞读操作,读操作也会阻塞写操作。
  2. 性能
    • 如果读操作远远多于写操作,RwLock性能更好,因为多个读操作可以并发执行。而Mutex无论读写都只能一个线程访问,在多读场景下性能较差。
    • 如果写操作频繁,MutexRwLock性能差异不大,因为RwLock的写操作也需要独占锁,并且可能因为等待读操作完成而有额外开销。

优先选择RwLock的场景举例

假设有一个应用程序,它有一个共享的配置文件数据结构。大部分时间,多个线程只是读取这个配置文件中的数据,只有在配置更新时才进行写操作。例如:

use std::sync::{Arc, RwLock};
use std::thread;

fn main() {
    let config = Arc::new(RwLock::new(Config { value: 42 }));

    let mut handles = vec![];
    for _ in 0..10 {
        let config_clone = config.clone();
        let handle = thread::spawn(move || {
            let config = config_clone.read().unwrap();
            println!("Read value: {}", config.value);
        });
        handles.push(handle);
    }

    let write_handle = thread::spawn(move || {
        let mut config = config.write().unwrap();
        config.value = 43;
        println!("Write new value: {}", config.value);
    });

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

struct Config {
    value: i32,
}

在这个例子中,由于读操作频繁(10个读线程),写操作较少(1个写线程),使用RwLock可以让读操作并发执行,提高整体性能。如果使用Mutex,读操作也只能串行执行,会降低效率。