MST

星途 面试题库

面试题:Rust Once初始化与线程安全的实践

在多线程环境下,`Once`类型如何保证初始化的线程安全性?请通过代码示例说明如何使用`Once`与`Arc`、`Mutex`等结合,对一个共享资源进行线程安全的初始化。阐述可能遇到的竞争条件以及如何避免。
33.5万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

1. Once类型保证初始化线程安全性的原理

Once类型通过内部的状态标志和同步机制来保证初始化的线程安全性。它使用了底层的原子操作来确保只有一个线程能够执行初始化操作,其他线程在初始化完成后直接获取结果,而不会重复执行初始化代码。

2. 代码示例

以下是使用OnceArcMutex结合对共享资源进行线程安全初始化的Rust代码示例:

use std::sync::{Arc, Once, Mutex};

static INIT: Once = Once::new();
static mut SHARED_RESOURCE: Option<Arc<Mutex<i32>>> = None;

fn get_shared_resource() -> Arc<Mutex<i32>> {
    INIT.call_once(|| {
        let resource = Arc::new(Mutex::new(42));
        unsafe {
            SHARED_RESOURCE = Some(resource.clone());
        }
    });
    unsafe {
        SHARED_RESOURCE.as_ref().unwrap().clone()
    }
}

fn main() {
    let handle1 = std::thread::spawn(|| {
        let resource = get_shared_resource();
        let mut data = resource.lock().unwrap();
        *data += 1;
        println!("Thread 1: {}", *data);
    });

    let handle2 = std::thread::spawn(|| {
        let resource = get_shared_resource();
        let mut data = resource.lock().unwrap();
        *data += 2;
        println!("Thread 2: {}", *data);
    });

    handle1.join().unwrap();
    handle2.join().unwrap();
}

3. 可能遇到的竞争条件及避免方法

  • 竞争条件:如果没有Once类型,多个线程可能同时尝试初始化共享资源,导致数据竞争和不一致。例如,两个线程同时创建Arc<Mutex<i32>>实例,使得共享资源状态混乱。
  • 避免方法:通过使用Once类型,利用其内部的同步机制(原子操作和状态标志),保证初始化操作只执行一次。只有第一个到达call_once的线程会执行初始化代码,其他线程等待初始化完成并直接获取结果,从而避免了竞争条件。同时,ArcMutex确保了共享资源在多线程间的安全访问,Arc用于共享所有权,Mutex用于互斥访问。