面试题答案
一键面试1. Rust中原子操作与happens - before关系
在Rust中,原子操作的顺序一致性(Ordering
)会影响happens - before
关系。原子操作通过std::sync::atomic
模块提供。
-
顺序一致性(
Ordering::SeqCst
):当使用Ordering::SeqCst
时,所有线程对原子变量的操作在全局都有一个一致的顺序。这意味着如果一个线程A对原子变量X进行写操作(store
),然后另一个线程B对X进行读操作(load
),且读操作看到了写操作的值,那么线程A的写操作happens - before
线程B的读操作。所有使用Ordering::SeqCst
的原子操作在所有线程间构成一个全序关系。 -
其他顺序(如
Relaxed
、Acquire
、Release
等):Relaxed
:只保证原子性,不保证任何同步或顺序关系,因此不建立happens - before
关系。Acquire
:读取操作建立一个acquire
屏障,确保在此读取之前的所有内存访问对当前线程可见。Release
:写入操作建立一个release
屏障,确保在此写入之后的所有内存访问对其他线程可见。当一个线程使用Release
进行写操作,另一个线程使用Acquire
进行读同一变量操作时,写操作happens - before
读操作。
2. futex底层机制对happens - before关系判断的作用
Futex(快速用户态互斥原语)是一种用户态和内核态混合的同步机制。
- Futex的基本原理:当竞争不激烈时,线程在用户态通过原子操作尝试获取锁(例如使用
compare - and - swap
操作),避免进入内核态的开销。当竞争激烈时,线程会陷入内核态等待,直到锁可用。 - 对happens - before关系的影响:当一个线程释放futex锁(通过
futex_wake
等操作),然后另一个线程获取该锁(通过futex_wait
等操作),释放锁的线程中在释放锁之前的所有内存访问对获取锁的线程可见,即释放锁的操作happens - before
获取锁的操作。这是因为futex内部实现依赖于原子操作和内核的等待队列机制,保证了这种同步关系。
3. 代码示例
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main() {
let shared_data = Arc::new(AtomicUsize::new(0));
let futex_mutex = Arc::new(Mutex::new(()));
let shared_data_clone1 = shared_data.clone();
let futex_mutex_clone1 = futex_mutex.clone();
let thread1 = thread::spawn(move || {
let _lock = futex_mutex_clone1.lock().unwrap();
shared_data_clone1.store(1, Ordering::SeqCst);
});
let shared_data_clone2 = shared_data.clone();
let futex_mutex_clone2 = futex_mutex.clone();
let thread2 = thread::spawn(move || {
let _lock = futex_mutex_clone2.lock().unwrap();
let value = shared_data_clone2.load(Ordering::SeqCst);
assert_eq!(value, 1);
});
thread1.join().unwrap();
thread2.join().unwrap();
}
4. 示例代码中happens - before关系分析
- 线程1:
- 获取futex_mutex锁
happens - before
对shared_data
进行store
操作。因为获取锁的操作(futex_wait
类似行为)与释放锁的操作(之前线程的futex_wake
类似行为)之间存在happens - before
关系,而这里是第一个获取锁,所以获取锁操作之前没有其他线程的释放锁操作,但它是后续store
操作的前置条件。 store
操作使用Ordering::SeqCst
,所以在全局顺序中,该store
操作在所有使用Ordering::SeqCst
的原子操作中有一个确定的顺序。
- 获取futex_mutex锁
- 线程2:
- 获取futex_mutex锁
happens - before
对shared_data
进行load
操作。 load
操作使用Ordering::SeqCst
,它会等待所有之前的Ordering::SeqCst
原子操作完成,所以线程1的store
操作happens - before
线程2的load
操作。
- 获取futex_mutex锁
总体来说,线程1获取锁、修改共享数据happens - before
线程2获取锁、读取共享数据,通过futex_mutex的同步和原子操作的顺序一致性保证了这种happens - before
关系。