Expand description
§XWOS RUST:线程
XWOS的线程分为 分离态 或 连接态 :
- 分离态(detached) :分离态的线程退出后由操作系统自动回收其内存资源;
- 连接态(joinable) :连接态的线程退出后需要被其他线程
join()
,之后才会被操作系统回收内存资源。
XWOS RUST的线程分为 动态线程 与 静态线程 。无论哪种线程创建时,都是 连接态(joinable) 的。 线程创建后,将返回线程的 Handle 。如果未将线程的 Handle 绑定到变量上,线程将自动转变为 分离态(detached) 。
§动态线程
动态线程 是指通过动态内存分配器创建的线程。
动态线程的对象结构体、栈、线程闭包等资源都是通过内存申请的接口动态创建的。
XWOS RUST的动态线程库是仿照 std::thread
来编写的。
use xwrust::xwos::thd;
thd::spawn(|_| {
// 线程代码;
});
在上述代码中,spawn()
方法会返回 DThdHandle
,但由于 DThdHandle
没有被绑定到任何变量名上,
其生命周期结束后的 drop()
方法会自动将动态线程转变为 分离态(detached) 。此线程运行结束后,其资源会被系统自动回收。
动态线程也可以是 可连接的(joinable) ,可以将返回的 DThdHandle
绑定在变量上。
然后通过 DThdHandle::join()
方法等待线程运行结束。
use xwrust::xwos::thd;
let res = thd::spawn(|_| {
// 线程代码;
// 返回值
});
match res {
Ok(handler) => {
match handler.join() {
Ok(r) => {
// `r` 是线程闭包的返回值。
},
Err(e) => {
// `join()` 失败时的错误码可通过 `e.state()` 获取。
// `e` 是 `DThdHandle<R>` ,重新被返回。
},
};
},
Err(rc) => {
// `rc` 是 `spawn()` 失败时的错误码。
},
};
DThdHandle::join()
方法会返回 [Result<R, Self>
] , R
是返回值的类型,并放在 Ok()
中,
方法调用失败时,会将 DThdHandle
放在 Err()
中重新返回。此时,用户可以通过 DThdHandle::state()
获取失败原因,
并且在合适的时机重新调用方法 DThdHandle::join()
。
§动态线程的线程函数
动态线程可以使用 FnOnce()
闭包作为线程函数。其原型是: FnOnce(Arc<DThdElement>) -> R
,
动态线程函数运行时,参数是动态线程的元素 DThdElement
,返回值为泛型 R
。
§动态线程的工厂模式
可以通过线程工厂 DThdBuilder
设置线程属性后,再创建动态线程:
use xwrust::xwos::thd;
let builder = thd::DThdBuilder::new()
.name("foo".into()) // 设置线程的名称
.stack_size(8 * 1024) // 设置线程栈大小
.privileged(true); // 设置系统权限
builder.spawn(|_| {
// 线程代码;
// 返回值
});
§线程的名称
线程工厂可以通过 DThdBuilder::name()
为线程指定一个字符串名字。可以为空,默认为 "anon"
。
§线程的栈大小
线程工厂可以通过 DThdBuilder::stack_size()
为线程指定栈内存大小,默认为XWOS的内核配置 XWMMCFG_STACK_SIZE_MIN
。
§线程的系统权限
某些SOC内部的寄存器,只有拥有系统权限的线程才可以访问。线程工厂可以通过 DThdBuilder::privileged()
为线程指定是否具有系统特权,默认为拥有系统权限。
§动态线程的元素
动态线程的元素 DThdElement
是存放与线程相关的信息的内存空间。
动态线程工厂 DThdBuilder
中设置的信息会被转移到 DThdElement
中。
动态线程运行时, Arc<DThdElement>
作为参数被传递到闭包内。以 Arc<T>
进行封装是因为要将其放在堆上。
§动态线程的句柄
静态线程的句柄 DThdHandle
功能类似于 std::thread::JoinHandle
,可用于控制动态子线程的退出。
§中断动态线程的阻塞态和睡眠态
方法 DThdHandle::intr()
可用于中断线程的 阻塞态 和 睡眠态 。
阻塞和睡眠的方法将以返回值 负 的 EINTR
退出。错误码 EINTR
会被转换为各个可阻塞线程的操作系统对象的错误码:
MutexError::Interrupt
SemError::Interrupt
CondError::Interrupt
FlgError::Interrupt
BrError::Interrupt
SelError::Interrupt
方法 DThdHandle::intr()
是基于 ThdD::intr()
实现的,调用后者与前者在功能上没有区别。
动态线程自身的 ThdD
可通过 cthd::i()
获取。
§通知动态线程退出
方法 DThdHandle::quit()
可用于父线程通知动态子线程退出。此方法不会等待动态子线程退出。
方法 DThdHandle::quit()
会为动态子线程设置 退出状态 ,并中断 阻塞状态 和 睡眠状态 。
阻塞和睡眠的方法将以返回值负的 EINTR
退出。错误码 EINTR
会被转换为各个可阻塞线程的操作系统对象的错误码:
MutexError::Interrupt
SemError::Interrupt
CondError::Interrupt
FlgError::Interrupt
BrError::Interrupt
SelError::Interrupt
但是,当动态子线程的 阻塞状态 是不可被中断的,方法 DThdHandle::quit()
只会为动态子线程设置 退出状态 ,不会发生中断。
动态子线程可以通过 cthd::shld_stop()
判断是否被设置了 退出状态 ,可以作为结束线程循环的条件。
方法 DThdHandle::quit()
是基于 ThdD::quit()
实现的,调用后者与前者在功能上没有区别。
动态线程自身的 ThdD
可通过 cthd::i()
获取。
§等待动态线程退出
当父线程需要等待动态子线程退出,并捕获其返回值时,需要使用方法 DThdHandle::join()
。
- 如果动态子线程还在运行,此方法会阻塞父线程直到动态子线程退出。
- 如果动态子线程已经提前运行至退出,此方法可立即返回动态子线程的返回值。
此方法会消费 DThdHandle
:
- 如果此方法执行成功,会消费掉
DThdHandle
,并将动态子线程的返回值放在Ok()
中返回,因为线程已经结束,其DThdHandle
的生命周期也应该结束; - 如果此方法执行失败,会重新在
Err()
中返回DThdHandle
,用户可通过DThdHandle::state()
方法获取失败的原因,并且在合适的重新调用DThdHandle::join()
方法。
§通知并等待动态线程退出
方法 DThdHandle::stop()
等价于 DThdHandle::quit()
+ DThdHandle::join()
§动态线程的示例
XWOS/xwam/xwrust-example/xwrust_example_dthd
§对比 std::thread
§闭包原型不同
std::thread
的闭包原型是FnOnce() -> R
。xwrust::xwos::thd
的闭包原型是FnOnce(Arc<DThdElement>) -> R
。- 【举例】
std::thread
通过thread::current()
获取线程自己的handle
,然后获取线程的名称:
use std::thread;
let handler = thread::DThdBuilder::new()
.name("named thread".into())
.spawn(|| {
let handle = thread::current();
assert_eq!(handle.name(), Some("named thread"));
}).unwrap();
handler.join().unwrap();
- 【举例】
xwrust::xwos::thd
通过闭包参数ele.name()
获取线程的名称:
use xwrust::xwos::thd;
use libc_print::std_name::println;
thd::DThdBuilder::new()
.name("foo".into())
.spawn(|ele| {
println!("Thread name: {}", ele.name());
});
若不需要使用 Arc<DThdElement>
,可使用 _
占位。
use xwrust::xwos::thd;
use libc_print::std_name::println;
thd::DThdBuilder::new()
.name("foo".into())
.spawn(|_| {
// 线程闭包
});
§spawn() 失败时的处理方式不同
std::thread
spawn() 失败时,会直接panic!()
;- XWOS是RTOS,运行环境是
#![no_std]
,panic!()
意味着死机,因此需要处理Err()
;
use xwrust::xwos::thd;
match thd::DThdBuilder::new()
.name("child".into())
.spawn(|ele| {
// 子线程闭包
}) {
Ok(h) => { // 新建子线程成功
},
Err(rc) => { // 新建子线程失败
},
};
§子线程 panic!()
的处理方式不同
std::thread
可捕获子线程的panic!()
。xwrust::xwos::thd
的子线程panic!()
后会导致整个系统 halt 。 目前#![no_std]
环境的 unwind 支持还不完善,暂时无法实现类似于std::thread
的捕获机制。
§静态线程
静态线程 是指 不 通过动态内存分配器创建的线程,所需内存全部由编译器在编译链接阶段分配。 设计 静态线程 的目的是为了避免使用动态内存分配。在功能安全的场合,动态内存分配是禁止使用的。
因为无法确定线程会运行多长时间,只能将 静态线程 定义为 静态生命周期 的全局变量。
use xwrust::xwos::thd::*;
static STHD: SThd<1024, &str> = SThd::new("SThd", true);
pub fn xwrust_example_sthd() {
let h = STHD.run(|sthd| { // 子线程
// 线程功能
"OK" // 返回值
});
let res = h.join();
match res {
Ok(r) => {
// `r` 是线程的返回值。
},
Err(e) => {
h = e;
// `join()` 失败时的错误码可通过 `e.state()` 获取。
// `e` 是 `SThdHandle` ,重新被返回。
},
};
}
静态线程可以是 可连接的(joinable) 。当另一线程通过 SThd::run()
启动静态线程后,
可将返回的 SThdHandle
绑定在变量上,然后通过 SThdHandle::join()
方法等待静态线程运行结束并获取返回值。
当 SThdHandle
生命周期结束前, SThdHandle::join()
未被调用过,其 drop()
方法会将静态线程自动变成 分离的(detached) 。
§静态线程的线程函数
定义RUST闭包时,实际上会生成一个隐藏的结构体,隐藏结构体中包括捕获的外部变量以及闭包函数。 闭包其实是这个隐藏结构体类型的变量,并且是在栈上建立的,其生命周期只限于函数局部。 因此,不能将闭包直接转移到静态线程的线程函数内部。
如何延长闭包生命周期?RUST语言的常用技巧是借助于 Box<T>
或 Arc<T>
将其放入堆中。
但若使用了 Box<T>
或 Arc<T>
就违背了 不使用动态内存分配 的初衷。
综上,静态线程的线程函数只能是 普通函数 或 不捕获任何环境的闭包 。
静态线程只能访问 静态生命周期的全局变量 。
其原型是 fn(&Self) -> R
。其中 &Self
是指向静态线程自身的引用, R
是泛型返回值。
§静态线程的句柄
静态线程的句柄 SThdHandle
由方法 SThd::run()
返回,功能类似于 std::thread::JoinHandle
,可用于控制静态子线程的退出。
§中断静态线程的阻塞态和睡眠态
方法 SThdHandle::intr()
可用于中断线程的 阻塞态 和 睡眠态 。
阻塞和睡眠的方法将以返回值 负 的 EINTR
退出。错误码 EINTR
会被转换为各个可阻塞线程的操作系统对象的错误码:
MutexError::Interrupt
SemError::Interrupt
CondError::Interrupt
FlgError::Interrupt
BrError::Interrupt
SelError::Interrupt
方法 SThdHandle::intr()
是基于 ThdD::intr()
实现的,调用后者与前者在功能上没有区别。
动态线程自身的 ThdD
可通过 cthd::i()
获取。
§通知动态线程退出
方法 SThdHandle::quit()
可用于父线程通知静态子线程退出。此方法不会等待静态子线程退出。
方法 SThdHandle::quit()
是基于 SThd::quit()
实现的,调用后者与前者在功能上没有区别。后者可以由静态现在自己调用,自己通知自己退出。
方法 ThdD::quit()
也可达到同样的效果。静态态线程自身的 ThdD
可通过 cthd::i()
获取。
通知静态线程退出的方法会为静态子线程设置 退出状态 ,并中断 阻塞状态 和 睡眠状态 。
阻塞和睡眠的方法将以返回值负的 EINTR
退出。错误码 EINTR
会被转换为各个可阻塞线程的操作系统对象的错误码:
MutexError::Interrupt
SemError::Interrupt
CondError::Interrupt
FlgError::Interrupt
BrError::Interrupt
SelError::Interrupt
但是,当静态子线程的 阻塞状态 是不可被中断的,方法 SThdHandle::quit()
只会为静态子线程设置 退出状态 ,不会发生中断。
静态子线程可以通过 cthd::shld_stop()
判断是否被设置了 退出状态 ,可以作为结束线程循环的条件。
§等待动态线程退出
当父线程需要等待静态子线程退出,并捕获其返回值,需要使用方法 SThdHandle::join()
。
- 如果静态子线程还在运行,此方法会阻塞父线程直到静态子线程退出。父线程的阻塞状态可被中断;
- 如果静态子线程已经提前运行至退出,此方法可立即返回静态子线程的返回值。
此方法会消费 SThdHandle
:
- 如果此方法执行成功,会消费掉
SThdHandle
,并将静态子线程的返回值放在Ok()
中返回,因为线程已经结束,其SThdHandle
的生命周期也应该结束; - 如果此方法执行失败,会重新在
Err()
中返回SThdHandle
,用户可通过SThdHandle::state()
方法获取失败的原因, 并且在合适的时机重新调用SThdHandle::join()
方法。
§通知并等待动态线程退出
方法 SThdHandle::stop()
等价于 SThdHandle::quit()
+ SThdHandle::join()
§静态线程的示例
XWOS/xwam/xwrust-example/xwrust_example_sthd
§只可在线程自身函数内部调用的方法
XWOS RUST有一组方法,只会对调用线程自身起作用,被实现在 xwrust::xwos::cthd
。命名中的 c 是 current 的意思。
Structs§
- 动态线程的工厂模式结构体,可用于配置新线程的属性
- 动态线程的元素
- 动态线程的句柄
- 静态线程对象结构体
- 静态线程的句柄
- XWOS的线程对象描述符
Enums§
- 线程的
join()/stop()
状态
Constants§
- XWOS线程对象占用的内存大小
Functions§
- 新建一个动态线程