面试题答案
一键面试使用原子类型的实现思路
- 初始化原子变量:使用
AtomicUsize
类型来存储当前分配的ID值,例如:
use std::sync::atomic::{AtomicUsize, Ordering};
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
- 分配ID函数:实现一个函数,通过原子操作获取并递增ID值。例如:
fn allocate_id() -> usize {
NEXT_ID.fetch_add(1, Ordering::SeqCst)
}
这里 fetch_add
方法会原子性地增加 NEXT_ID
的值,并返回增加前的值,作为分配的ID。
使用锁机制的实现思路
- 初始化锁和ID变量:使用
Mutex
或RwLock
来保护共享的ID变量。例如:
use std::sync::{Mutex, RwLock};
static mut NEXT_ID: usize = 0;
static LOCK: Mutex<()> = Mutex::new(());
- 分配ID函数:在函数中获取锁,然后对ID变量进行操作。例如:
fn allocate_id() -> usize {
let _lock_guard = LOCK.lock().unwrap();
unsafe {
NEXT_ID += 1;
NEXT_ID - 1
}
}
这里通过获取 Mutex
的锁,保证在同一时间只有一个线程可以访问和修改 NEXT_ID
变量。
AtomicUsize
在该场景下解决的问题
- 线程安全:
AtomicUsize
提供了原子操作,确保在多线程环境下对ID的分配和递增操作是线程安全的,无需额外的锁机制来同步访问,从而避免了锁带来的性能开销和死锁风险。 - 高效性:原子操作通常比锁机制更高效,特别是在高并发场景下。由于原子操作是由硬件直接支持的,在不同线程对
AtomicUsize
进行操作时,不需要像锁机制那样进行上下文切换和等待锁的释放,大大提高了ID分配的效率。