Expand description
§XWOS RUST:互斥锁
互斥锁是用来保证不同线程正确访问共享数据的机制。访问共享数据的代码片段被称为临界区。
互斥锁 只能 用在 线程上下文(Thread Context) 。等待互斥锁的线程会被阻塞,并让出CPU的使用权。
XWOS RUST框架的互斥锁是仿造 std::sync::Mutex
的来编写的。
互斥锁上锁后,可返回一个 守卫 MutexGuard
,用于提供 Scoped Lock 机制:即只负责上锁,不用关心解锁。
解锁会由 MutexGuard
在其生命周期结束后自动触发。
§创建
XWOS RUST的互斥锁可使用 Mutex::new()
创建。
- 可以创建具有静态生命周期
static
约束的全局变量:
use xwrust::xwos::lock::mtx::*;
static GLOBAL_MUTEX: Mutex<u32> = Mutex::new(0);
- 也可以使用
alloc::sync::Arc
在heap中创建:
extern crate alloc;
use alloc::sync::Arc;
use xwrust::xwos::lock::mtx::*;
pub fn xwrust_example_mutex() {
let mutex: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
}
§初始化
无论以何种方式创建的互斥锁,都必须在使用前调用 Mutex::init()
进行初始化:
pub fn xwrust_example_mutex() {
GLOBAL_MUTEX.init();
mutex.init();
}
§解锁
上锁后返回的 MutexGuard
。 MutexGuard
的生命周期结束时,会自动解锁。
也可调用 Mutex::unlock()
主动消费掉 MutexGuard
来解锁。
§上锁
§普通等待上锁
Mutex::lock()
方法只可在 线程 上下文中使用:
- 若线程无法获得锁,会阻塞等待。
- 当锁的占用者解锁时,锁会唤醒优先级最高的线程,并让此线程获得锁。
- 线程获得锁后返回
Ok()
,并在其中包含锁的守卫MutexGuard
。 - 当线程阻塞等待被中断时,会在
Err()
中返回MutexError::Interrupt
。
§超时等待上锁
Mutex::lock_to()
方法只可在 线程 上下文中使用:
- 若线程无法获得锁,会阻塞等待,等待时会指定一个唤醒时间点。
- 当锁的占用者解锁时,锁会唤醒优先级最高的线程,并让此线程获得锁。
- 线程获得锁后返回
Ok()
,并在其中包含锁的守卫MutexGuard
。 - 当线程阻塞等待被中断时,会在
Err()
中返回MutexError::Interrupt
。 - 当到达指定的唤醒时间点,线程被唤醒,并返回
MutexError::Timedout
。
§不可中断等待上锁
Mutex::lock_unintr()
方法只可在 线程 上下文中使用:
- 若线程无法获得锁,会阻塞等待,且不可被中断,也不会超时。
- 当锁的占用者解锁时,锁会唤醒优先级最高的线程,并让此线程获得锁。
- 线程获得锁后返回
Ok()
,并在其中包含锁的守卫MutexGuard
。
§尝试等待上锁
Mutex::trylock()
方法只可在 线程 上下文中使用,不会阻塞线程,只会检测锁是否可被获取:
- 若线程可获得锁,立即获得锁并在
Ok()
中返回锁的守卫MutexGuard
。 - 若线程无法获得锁,立即在
Err()
中返回MutexError::WouldBlock
。
§示例
XWOS/xwam/xwrust-example/xwrust_example_mutex
§对比 std::sync::Mutex
- XWOS RUST
use xwrust::xwos::thd;
use xwrust::xwos::lock::mtx::*;
extern crate alloc;
use alloc::sync::Arc;
pub fn xwrust_example_mutex() {
// ...省略...
let lock: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
let lock_child = lock.clone();
match lock.lock() {
Ok(mut guard) => { // 主线程上锁成功
*guard = 1; // 访问共享变量
}
Err(e) => {
// 主线程上锁失败
}
}
// ...省略...
thd::Builder::new()
.name("child".into())
.spawn(move |_| {
// 子线程闭包
match lock_child.lock() {
Ok(mut guard) => { // 子线程上锁成功
*guard += 1;
}
Err(e) => { // 子线程上锁失败
}
}
});
}
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// 创建互斥锁的方法类似
let lock = Arc::new(Mutex::new(0_u32));
let lock2 = Arc::clone(&lock);
let _ = thread::spawn(move || -> () {
// 访问共享数据的方法类似:
// 子线程中对互斥锁上锁, unwrap() 从 Ok() 中取出 guard,再对 guard 解可变引用可得数据的可变引用
let guard = lock2.lock().unwrap();
*guard = 1;
// std库特有的机制:持有锁时 panic!() 将导致锁变成 **中毒状态(poisoned)** 。
panic!();
}).join();
// std库特有的机制:处理中毒状态的锁
let mut guard = match lock.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
*guard += 1;
}
§构建全局变量的方式
std::sync::Mutex
没有编译期构造函数,因此只能借助宏lazy_static!
创建static
约束的全局变量;xwrust::xwos::lock::mtx
则可以直接在函数外部定义。
§中毒锁机制
std::sync::Mutex
提供了 Poisoned Mutex 机制。 当通过std::thread::spawn()
创建的子线程在持有 互斥锁 时,发生了panic!()
, 此时 互斥锁 的状态被称为 中毒(Poisoned) 。std::sync::Mutex
可在父线程中检测到此错误状态,并尝试恢复。xwrust::xwos::lock::mtx
不提供此机制,用户必须处理Ok()
与Err()
,不可使用unwrap()
。 因为此机制需要依赖 unwind 机制 ,目前 unwind 在MCU上还不成熟:- Gcc可以在MCU C++中使用 try-catch;
- LLVM(Clan++)还无法支持在MCU C++中使用 try-catch;
- Rust目前在MCU上还无法使用 panic_unwind 的 feature。
Structs§
- 互斥锁对象结构体
- 互斥锁对象的RAII Guard
Enums§
- 互斥锁的错误码
Constants§
- XWOS互斥锁对象占用的内存大小