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::threadspawn() 失败时,会直接- 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§
- 新建一个动态线程