备注
作者:龙进 longjin@RinGoTek.cn
mutex互斥量
mutex是一种轻量级的同步原语,只有被加锁、空闲两种状态。
当mutex被占用时,尝试对mutex进行加锁操作的进程将会被休眠,直到资源可用。
1. 特性
同一时间只有1个任务可以持有mutex
不允许递归地加锁、解锁
只允许通过mutex的api来操作mutex
在硬中断、软中断中不能使用mutex
2. 定义
mutex定义在lib/mutex.rs
中,定义如下所示:
/// @brief Mutex互斥量结构体
/// 请注意!由于Mutex属于休眠锁,因此,如果您的代码可能在中断上下文内执行,请勿采用Mutex!
#[derive(Debug)]
pub struct Mutex<T> {
/// 该Mutex保护的数据
data: UnsafeCell<T>,
/// Mutex内部的信息
inner: SpinLock<MutexInner>,
}
#[derive(Debug)]
struct MutexInner {
/// 当前Mutex是否已经被上锁(上锁时,为true)
is_locked: bool,
/// 等待获得这个锁的进程的链表
wait_list: LinkedList<&'static mut process_control_block>,
}
3. 使用
与SpinLock类似,Rust版本的Mutex具有一个守卫。使用的时候,需要将要被保护的数据的所有权移交Mutex。并且,守卫只能在加锁成功后产生,因此,每个时刻,每个Mutex最多存在1个守卫。
当需要读取、修改Mutex保护的数据时,请先使用Mutex的lock()
方法。该方法会返回一个MutexGuard
。您可以使用被保护的数据的成员函数来进行一些操作。或者是直接读取、写入被保护的数据。(相当于您获得了被保护的数据的可变引用)
完整示例如下方代码所示:
let x :Mutex<Vec<i32>>= Mutex::new(Vec::new());
{
let mut g :MutexGuard<Vec<i32>>= x.lock();
g.push(1);
g.push(2);
g.push(2);
assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]);
// 在此处,Mutex是加锁的状态
debug!("x={:?}", x);
}
// 由于上方的变量`g`,也就是Mutex守卫的生命周期结束,自动释放了Mutex。因此,在此处,Mutex是放锁的状态
debug!("x={:?}", x);
对于结构体内部的变量,我们可以使用Mutex进行细粒度的加锁,也就是使用Mutex包裹需要细致加锁的成员变量,比如这样:
pub struct a {
pub data: Mutex<data_struct>,
}
当然,我们也可以对整个结构体进行加锁:
struct MyStruct {
pub data: data_struct,
}
/// 被全局加锁的结构体
pub struct LockedMyStruct(Mutex<MyStruct>);
4. API
4.1. new - 初始化Mutex
原型
pub const fn new(value: T) -> Self
说明
new()
方法用于初始化一个Mutex。该方法需要一个被保护的数据作为参数。并且,该方法会返回一个Mutex。