实现思路
- 数据存储:使用
UnsafeCell
来存储需要保护的数据,因为UnsafeCell
允许内部可变性,这是实现类似Mutex
功能的基础。
- 原子操作:利用Rust的原子类型(如
AtomicBool
)来实现锁的状态管理。AtomicBool
提供了原子的布尔操作,用于表示锁是否被持有。
- 释放和获取顺序原则:通过原子操作的
Ordering
参数来确保遵循释放和获取顺序。在释放锁时使用Release
顺序,在获取锁时使用Acquire
顺序,这样可以保证在释放锁之前对数据的所有写操作对获取锁之后的读操作可见。
- 避免内存安全问题和数据竞争:
- 内存安全问题:只在
unsafe
块中访问UnsafeCell
内部的数据,并且在访问前确保锁已被获取。
- 数据竞争:通过锁机制(原子类型实现的锁)来避免多个线程同时访问被保护的数据。
关键代码片段
use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicBool, Ordering};
struct MyMutex<T> {
data: UnsafeCell<T>,
locked: AtomicBool,
}
impl<T> MyMutex<T> {
pub fn new(data: T) -> Self {
MyMutex {
data: UnsafeCell::new(data),
locked: AtomicBool::new(false),
}
}
pub fn lock(&self) {
while self.locked.swap(true, Ordering::Acquire) {
// 自旋等待锁释放
}
}
pub fn unlock(&self) {
self.locked.store(false, Ordering::Release);
}
pub fn get_mut(&self) -> &mut T {
self.lock();
// SAFETY: 因为已经获取锁,所以这里访问UnsafeCell内部数据是安全的
unsafe { &mut *self.data.get() }
}
}
说明
MyMutex
结构体:包含一个UnsafeCell<T>
用于存储数据,和一个AtomicBool
用于表示锁的状态。
new
方法:初始化MyMutex
实例,将locked
初始化为false
。
lock
方法:使用AtomicBool
的swap
方法尝试获取锁,若锁已被持有则自旋等待。这里swap
使用Ordering::Acquire
顺序。
unlock
方法:使用AtomicBool
的store
方法释放锁,使用Ordering::Release
顺序。
get_mut
方法:先获取锁,然后通过unsafe
块返回内部数据的可变引用。因为获取锁保证了只有一个线程能访问内部数据,从而避免了数据竞争和内存安全问题。