RT_Thread——线程管理(上)
文章目录
- 一、基本概念
- 二、线程创建与删除
- 2.1 线程的组成
- 2.1.1 线程控制块
- 2.1.2 线程栈
- 2.1.3 入口函数
- 2.2 创建、启动线程
- 2.3 创建线程示例
- 2.4 使用线程参数示例
- 2.5 线程的删除
- 2.6 删除线程示例
- 三、线程优先级和 Tick
- 3.1 线程优先级
- 3.2 时间片
- 3.3 优先级实验
- 3.4 修改优先级
- 四、线程状态
- 4.1 初始状态
- 4.2 就绪状态
- 4.3 运行状态
- 4.4 挂起状态
- 4.5 关闭状态
- 4.6 完整的状态转换图
一、基本概念
对于整个单片机程序,我们称之为 application,应用程序。
使用 RT-Thread 时,我们可以在 application 中创建多个线程(thread),有些文档把线程也称为任务(task)。
以日常生活为例,比如这个母亲要同时做两件事:
- 喂饭:这是一个线程
- 回信息:这是另一个线程
这可以引入很多概念:
- 线程状态(State):
- 当前正在喂饭,它是 running 状态;另一个" 回信息" 的线程就是"not running"状态
- "not running"状态还可以细分:
- init:初始状态
- ready:就绪状态,随时可以运行
- suspended:挂起状态,等待某些资源,暂时不能运行
- close:关闭状态,线程退出
- 优先级(Priority)
- 我工作生活兼顾:喂饭、回信息优先级一样,轮流做
- 我忙里偷闲:还有空闲线程,休息一下
- 厨房着火了,什么都别说了,先灭火:优先级更高
- 栈(Stack)
- 喂小孩时,我要记得上一口喂了米饭,这口要喂青菜了
- 回信息时,我要记得刚才聊的是啥
- 做不同的线程,这些细节不一样
- 对于人来说,当然是记在脑子里
- 对于程序,是记在栈里
- 每个线程有自己的栈
- 事件驱动
- 孩子吃饭太慢:先休息一会,等他咽下去了、等他提醒我了,再喂下一口
二、线程创建与删除
2.1 线程的组成
在 RT-Thread 中,线程是 RT-Thread 中最基本的调度单位,使用 rt_thread
结构体表示线程。
rt_thread 描述了一个线程执行的运行环境,也描述了这个线程所处的优先等级。
系统中总共存在两类线程,分别是系统线程和用户线程
- 系统线程由 RT-Thread 内核创建
- 用户线程由用户应用程序创建
这两类线程都会从内核对象容器中分配线程对象,如下图所示。
每个线程由三部分组成:线程控制块(rt_thread 结构体)、线程栈和入口函数。
2.1.1 线程控制块
线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构。
它存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等。
它在 rtdef.h 中定义如下:
/**
* Thread structure
*/
struct rt_thread
{/* rt 对象 */char name[RT_NAME_MAX]; /* 线程名字 */rt_uint8_t type; /* 对象类型*/rt_uint8_t flags; /* 标注位*/rt_list_t list; /* 对象列表 */rt_list_t tlist; /* 线程列表*//* 栈指针和入口指针 */void *sp; /* 栈指针*/void *entry; /* 入口函数指针*/void *parameter; /* 参数*/void *stack_addr; /* 栈地址指针*/rt_uint32_t stack_size; /* 栈大小*//* 错误代码*/rt_err_t error; /* 线程错误代码 */rt_uint8_t stat; /* 线程状态*//* 优先级 */rt_uint8_t current_priority; /* 当前优先级*/rt_uint8_t init_priority; /* 初始优先级*/…………rt_ubase_t init_tick; /* 线程初始化计数值*/rt_ubase_t remaining_tick; /* 线程剩余计数值*/struct rt_timer thread_timer;/* 内置线程定时器*/void (*cleanup)(struct rt_thread *tid);/* 线程退出清理函数*/rt_uint32_t user_data;/* 用户私有数据*/
};
typedef struct rt_thread *rt_thread_t;
2.1.2 线程栈
在裸机系统中, 涉及局部变量、子函数调用或中断发生,就需要用到栈。
在 RTOS 系统中,每个线程运行时,也是普通的函数调用,也涉及局部变量、子函数调用、中断,也要用到栈。
但不同于裸机系统,RTOS 存在多个线程,每个线程是独立互不干扰的,因此需要为每个线程都分配独立的栈空间,这就是线程栈。
可以使用两种方法提供线程栈:静态分配、动态分配。栈的大小通常由用户定义,如下使用全局数组提供了一个静态栈,大小为 512 字节:
rt_uint32_t test_stack[512];
对于资源比较大的 MCU,可以适当设置较大的线程栈。
也可以在初始化时设置为较大的栈,比如 1K 或 2K,在进入系统后,通过终端的 list_thread
命令查看当前线程栈的最大使用率。如果使用率超过 70%,将线程栈再设置大一点;如果远低于 70%,将线程栈设置小一点。
2.1.3 入口函数
入口函数是线程要运行函数,由用户自行设计。
可分为无限循环模式和顺序执行模式。
- 无限循环模式:
void thread_entry(void* paramenter)
{while (1){/* 等待事件的发生*//* 对事件进行服务、进行处理*/}
}
使用这种模式时,需要注意,一个实时系统,不应该让一个线程一直处于最高优先级占用 CPU,让其它线程得不到执行。
因此,在这种模式中,需要调用延时函数或者主动挂起。
这种无限循环模式设计的目的是让这个线程一直循环调度运行,而不结束。
- 顺序执行模式:
static void thread_entry(void* parameter)
{/* 处理事务#1 */…/* 处理事务#2 */…/* 处理事务#3 */
}
使用这种模式时,线程不会一直循环,最后一定会执行完毕。
执行完毕后,线程将被系统自动删除。
2.2 创建、启动线程
RT-Thread 提供两种线程的创建方式:
- 静态线程:使用
rt_thread_init()
初始化 - 动态线程:使用
rt_thread_create()
创建
区别:动态线程是系统自动从动态内存堆上分配栈空间与线程句柄,静态线程是由用户分配栈空间与线程句柄
- 静态线程初始化函数如下:
rt_err_t rt_thread_init{struct rt_thread* thread, //线程句柄const char* name, //线程名字void (*entry)(void* parameter), //入口函数void* parameter, //入口函数参数void* stack_start, //线程栈起始地址rt_uint32_t stack_size, //栈大小rt_uint8_t priority, //线程优先级rt_uint32_t tick}; //线程时间片大小
参数说明:
- 动态线程创建函数如下:
rt_thread_t rt_thread_create{const char* name, //线程名字void (*entry)(void* parameter), //入口函数void* parameter, //入口函数参数rt_uint32_t stack_size, //栈大小rt_uint8_t priority, //线程优先级rt_uint32_t tick}; //线程时间片大小
参数说明:
创建线程后,还需要启动线程,才能让线程运行起来。
- 启动线程函数如下:
rt_err_t rt_thread_startup(rt_thread_t thread);
参数说明:
2.3 创建线程示例
代码为:RT-Thread_01_create_task
使用两种方式分别创建两个线程。
线程 1 的代码:
/* 线程1的入口函数 */
static void thread1_entry(void *parameter)
{const char *thread_name = "Thread1 run\r\n";volatile rt_uint32_t cnt = 0;/* 线程1 */while(1){/* 打印线程1的信息 */rt_kprintf(thread_name);/* 延迟一会(比较简单粗暴) */for( cnt = 0; cnt < 100000; cnt++ ){}}
}
线程 2 的代码:
/* 线程2入口函数 */
static void thread2_entry(void *param)
{const char *thread_name = "Thread2 run\r\n";volatile rt_uint32_t cnt = 0;/* 线程2 */while(1){/* 打印线程2的信息 */rt_kprintf(thread_name);/* 延迟一会(比较简单粗暴) */for( cnt = 0; cnt < 100000; cnt++ ){}}
}
main 函数:
int main(void)
{/* 初始化静态线程1,名称是Thread1,入口是thread1_entry */rt_thread_init(&thread1, //线程句柄 "thread1", //线程名字thread1_entry, //入口函数RT_NULL, //入口函数参数&thread1_stack[0], //线程栈起始地址sizeof(thread1_stack), //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小 /* 启动线程1 */rt_thread_startup(&thread1); /* 创建动态线程2,名称是thread2,入口是thread2_entry*/thread2 = rt_thread_create("thread2", //线程名字thread2_entry, //入口函数RT_NULL, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程2 */if (thread2 != RT_NULL)rt_thread_startup(thread2); return 0;
}
运行结果如下:
注意:
- 线程 1 是静态初始化,线程 2 是动态初始化
- 它们的优先级,时间片大小都设置一样
线程运行图:
- thread1 和 thread2 优先级相同,因此不存在抢占
- 在 t1,thread1 进入运行态,一直运行直到 t2;这个时间长度就是 thread1
设置的时间片长度,15 个 Tick 时钟节拍 - 在 t2,thread2 进入运行态,一直运行直到 t3;这个时间长度就是 thread2
设置的时间片长度,15 个 Tick 时钟节拍 - 在 t3,thread1 重新进入运行态,如此交替
2.4 使用线程参数示例
代码为:RT-Thread_02_create_task_use_params
多个线程可以使用同一个函数,怎么体现它们的差别?
- 栈不同
- 创建线程时可以传入不同的参数
我们创建 2 个线程,使用同一个函数,代码如下:
/* 线程的入口函数 */
static void thread1_entry(void *parameter)
{const char *thread_name = parameter;volatile rt_uint32_t cnt = 0;/* 线程 */while(1){/* 打印线程的信息 */rt_kprintf(thread_name);/* 延迟一会(比较简单粗暴) */for( cnt = 0; cnt < 100000; cnt++ ){}}
}
上述代码中的 thread_name
来自参数 parameter
,parameter 来自哪里?创建线程时
传入的。
代码如下:
- 使用
rt_thread_init
和rt_thread_create
分别创建线程时,传入不同的函数参数 - 不同的线程,parameter 不一样
static const char *thread1_name = "Thread1 run\r\n";
static const char *thread2_name = "Thread2 run\r\n";int main(void)
{/* 初始化静态线程1,名称是Thread1,入口是thread1_entry */rt_thread_init(&thread1, //线程句柄 "thread1", //线程名字thread1_entry, //入口函数(void *)thread1_name, //入口函数参数&thread1_stack[0], //线程栈起始地址sizeof(thread1_stack), //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小 /* 启动线程1 */rt_thread_startup(&thread1); /* 创建动态线程2,名称是thread2,入口也是thread1_entry*/thread2 = rt_thread_create("thread2", //线程名字thread1_entry, //入口函数(void *)thread2_name, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程2 */if (thread2 != RT_NULL)rt_thread_startup(thread2); return 0;
}
结果与2.3中的一样。
2.5 线程的删除
创建线程时有 2 种方式,删除线程时也有对应的函数:
rt_err_t rt_thread_detach (rt_thread_t thread); // 删除使用rt_thread_init()创建的线程rt_err_t rt_thread_delete(rt_thread_t thread); // 删除使用rt_thread_create()创建的线程
参数说明:
2.6 删除线程示例
代码为:RT-Thread_03_delete_task
- 先分别使用两种方式创建线程
- 然后再分别删除线程线程创建代码如下:
线程的创建和初始化):
/* 初始化静态线程1,名称是Thread1,入口是thread1_entry */
rt_thread_init(&thread1, //线程句柄 "thread1", //线程名字thread1_entry, //入口函数(void *)thread1_name, //入口函数参数&thread1_stack[0], //线程栈起始地址sizeof(thread1_stack), //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小
/* 启动线程1 */
rt_thread_startup(&thread1); /* 创建动态线程2,名称是thread2,入口也是thread1_entry*/
thread2 = rt_thread_create("thread2", //线程名字thread1_entry, //入口函数(void *)thread2_name, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程2 */
if (thread2 != RT_NULL)rt_thread_startup(thread2);
线程删除代码如下:
/* 延时一段时间后再删除线程 */
rt_thread_mdelay(100);/* 删除线程1 */
result = rt_thread_detach(&thread1);
if(result == RT_EOK)rt_kprintf("Thread1 exit\r\n");/* 删除线程2 */
result = rt_thread_delete(thread2);
if(result == RT_EOK)rt_kprintf("Thread2 exit\r\n");
运行结果如下:
线程运行细节:
rt_thread_delete
并不是真正的删除线程, 只是把线程状态状态改为
RT_THREAD_CLOSE
。- 真正的删除(释放线程控制块和线程栈),在下一次执行空闲线程时,由空闲线程删除
- 线程本身不应调用 rt_thread_detach 脱离线程
三、线程优先级和 Tick
3.1 线程优先级
RT-Thread 的线程优先级是指线程被调度的优先程度。
每个线程都具有优先级,线程的重要性越高,优先级应该设置更高,被调度的可能才会更大。
由 rtconfig.h
中定义的 RT_THREAD_PRIORITY_MAX
宏指定优先级范围。
RT-Thread 最大支持 256
个线程优先级 (0~255)
,数值越小的优先级越高,0
为最高优先级。
在一些资源比较紧张的系统中,可以根据实际情况设置优先级,比如 ARM Cortex-M
系列,通常采用 32 个优先级。
最低优先级
默认分配给空闲线程使用,用户一般不使用。*
在学习调度方法之前,只要初略地知道:
- RT-Thread 会确保最高优先级的、可运行的线程,马上就能执行
- 对于相同优先级的、可运行的线程,轮流执行
这无需记忆,就像我们举的例子:
- 厨房着火了,当然优先灭火
- 喂饭、回复信息同样重要,轮流做
3.2 时间片
对于同优先级的线程,它们“轮流”执行。怎么轮流?你执行一会,我执行一会。
"一会"怎么定义?
人有心跳,心跳间隔基本恒定。
RT-Thread 中也有心跳,它使用定时器产生固定间隔的中断。这叫 Tick
、滴答,比如每 1ms 发生一次时钟中断。
如下图:
- 假设 t1、t2、t3 发生时钟中断
- 两次中断之间的时间被称为时间片(
time slice
、tick period
) - 时间片的大小在
rtconfig.h
中定义,默认为#define RT_TICK_PER_SECOND 1000
,即1ms
假设有 2 个优先级相同的就绪态线程 A 与 B。
A 线程的时间片设置为 10,B 线程的时间片设置为 5。
且当系统中不存在比 A、B 优先级高的就绪态线程时,系统会在 A、B 线程间来回切换执行。
在 t1 至 t1+5 这 5 个时间片,线程 B 运行
在 t1+5 至 t1+15 这 10 个时间片,线程 A 运行如此反复,如下图
3.3 优先级实验
代码为:RT-Thread_04_task_priority本程序会创建 3 个线程:
- 线程 1、线程 2:优先级相同,都是 15
- 线程 3:优先级最高,是 15-1=14
创建线程时使用同一个入口函数,但是通过传入不同的参数、不同的优先级生成不同的线程:
- thread1、thread2 的优先级相同,都是
THREAD_PRIORITY
- thread3 的优先级最高,是
THREAD_PRIORITY-1
程序开头的一些定义:
#include <rtthread.h>
#include <rtdevice.h>#define THREAD_PRIORITY 15 //设置线程优先级
#define THREAD_STACK_SIZE 512 //设置线程栈大小
#define THREAD_TIMESLICE 15 //设置线程时间片大小static struct rt_thread *thread1; //定义线程1句柄指针
static struct rt_thread *thread2; //定义线程2句柄指针
static struct rt_thread *thread3; //定义线程3句柄指针
入口函数代码如下:
/* 线程的入口函数 */
static void thread_entry(void *parameter)
{const char *thread_name = parameter;volatile rt_uint32_t cnt = 0;/* 线程 */while(1){/* 打印线程的信息 */rt_kprintf(thread_name);rt_thread_delay(100);}
}
main 函数代码如下:
static const char *thread1_name = "Thread1 run \r\n";
static const char *thread2_name = "Thread2 run \r\n";
static const char *thread3_name = "Thread3 run \r\n";int main(void)
{/* 创建动态线程thread1,优先级为 THREAD_PRIORIT = 15 */thread1 = rt_thread_create("thread1", //线程名字thread_entry, //入口函数(void *)thread1_name, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程1 */if (thread1 != RT_NULL)rt_thread_startup(thread1); /* 创建动态线程thread2,优先级为 THREAD_PRIORIT = 15 */thread2 = rt_thread_create("thread2", //线程名字thread_entry, //入口函数(void *)thread2_name, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程2 */if (thread2 != RT_NULL)rt_thread_startup(thread2); /* 创建动态线程thread3,优先级为 THREAD_PRIORIT-1 = 14 */thread3 = rt_thread_create("thread3", //线程名字thread_entry, //入口函数(void *)thread3_name, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY-1, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程3 */if (thread3 != RT_NULL)rt_thread_startup(thread3); return 0;
}
运行情况如下图所示:
- 线程 3 优先执行,直到它调用
rt_thread_delay
延时,放弃 CPU 占用 - 线程 1、线程 2:轮流执行
3.4 修改优先级
本节代码为:RT-Thread_05_change_priority。
线程的优先级保存在线程控制块中,通过 thread->current_priority
获取线程的优先级
程序开头的定义:
#include <rtthread.h>
#include <rtdevice.h>#define THREAD_PRIORITY 15 //设置线程优先级
#define THREAD_STACK_SIZE 512 //设置线程栈大小
#define THREAD_TIMESLICE 15 //设置线程时间片大小static struct rt_thread *thread1; //定义线程1句柄指针
static struct rt_thread *thread2; //定义线程2句柄指针
main 函数的代码如下,它创建了 2 个线程:线程 1 的优先级更高,它先执行:
static const char *thread1_name = "Thread1 run";
static const char *thread2_name = "Thread2 run";int main(void)
{/* 创建动态线程thread1,优先级为 THREAD_PRIORIT-1 = 14 */thread1 = rt_thread_create("thread1", //线程名字thread1_entry, //入口函数(void *)thread1_name, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY-1, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程1 */if (thread1 != RT_NULL)rt_thread_startup(thread1); /* 创建动态线程thread2,优先级为 THREAD_PRIORIT = 15 */thread2 = rt_thread_create("thread2", //线程名字thread2_entry, //入口函数(void *)thread2_name, //入口函数参数THREAD_STACK_SIZE, //栈大小THREAD_PRIORITY, //线程优先级THREAD_TIMESLICE); //线程时间片大小/* 判断创建结果,再启动线程2 */if (thread2 != RT_NULL)rt_thread_startup(thread2); return 0;
}
线程 1 的代码如下:
/* 线程1的入口函数 */
static void thread1_entry(void *parameter)
{const char *thread_name = parameter;volatile rt_uint32_t cnt = 0;rt_uint8_t priority;/* 线程 */while(1){/* 打印线程的信息 */rt_kprintf("%s: thread1 priority is %d, thread2 priority is %d\n\r", thread_name, thread1->current_priority, thread2->current_priority);if (thread1->current_priority <= thread2->current_priority){priority = thread2->current_priority - 1;rt_thread_control(thread2, RT_THREAD_CTRL_CHANGE_PRIORITY, &priority);rt_schedule();}}
}
线程 2 的代码如下:
/* 线程2的入口函数 */
static void thread2_entry(void *parameter)
{const char *thread_name = parameter;volatile rt_uint32_t cnt = 0;/* 线程 */while(1){/* 打印线程的信息 */rt_kprintf("%s: thread1 priority is %d, thread2 priority is %d\n\r", thread_name, thread1->current_priority, thread2->current_priority);}
}
调度情况如下图所示:
- 1:一开始 thread1 优先级最高,它先执行,它提升了 thread2 的优先级。
- 2:thread2 的优先级最高,它再执行。
其中 rt_thread_control
函数解析如下:
rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void* arg);
指示控制命令cmd当前支持的命令包括
RT_THREAD_CTRL_CHANGE_PRIORITY
- 动态更改线程的优先级;RT_THREAD_CTRL_STARTUP
- 开始运行一个线程,等同于rt_thread_startup()
函数调用;RT_THREAD_CTRL_CLOSE
- 关闭一个线程,等同于rt_thread_delete()
函数调用。
void rt_schedule
函数解析如下:
void rt_schedule(void);
调用后,系统计算一次系统中就绪态的线程,如果存在比当前线程更高优先级的线程时,系统将切换到高优先级的线程去。
四、线程状态
以前我们很简单地把线程的状态分为 2 种:运行(Runing)、非运行(Not Running)。
对于非运行的状态,还可以继续细分,比如前面的 RT-Thread_04_task_priority 中:
- Task3 执行 rt_thread_delay 后:处于非运行状态,要等延时结束才能再次运行
- Task3 运行期间,Task1、Task2 也处于非运行状态,但是它们随时可以运行
- 这两种"非运行"状态就不一样,可以细分为:
- 挂起状态,也称阻塞态:等待某些资源
- 就绪状态:随时可以运行
- 还有其他状态:
- 初始状态:线程刚被创建时处于初始状态
- 关闭状态:线程退出
4.1 初始状态
当线程刚开始创建还没开始运行时就处于初始状态:
- 使用
rt_thread_init()
创建,但是未调用rt_thread_startup
使它就绪 - 使用
rt_thread_create()
创建,但是未调用rt_thread_startup
使它就绪
4.2 就绪状态
这个线程完全准备好了,随时可以运行:只是还轮不到它:这时它就处于就绪态
(Ready)。
在下面几种情况下,线程都处于就绪状态:
- 我们创建线程后,使用
rt_thread_startup()
函数使它进入就绪态。 - 它在运行过程中,被更高优先级的线程抢占了,这时它处于就绪状态。
- 它在运行过程中,轮到同优先级的线程运行了,这时它处于就绪状态。
- 它因为等待某些资源而没有运行,别的线程或者中断函数把它唤醒了,这时它处于就绪状态。
4.3 运行状态
当处于就绪状态的线程运行时,它就处于运行状态。
4.4 挂起状态
在日常生活的例子中,母亲在电脑前跟同事沟通时,如果同事一直没回复,那么母亲的工作就被卡住了、被堵住了、处于挂起状态。
重点在于:母亲在等待。
在 RT-Thread_04_task_priority 实验中,如果把线程 3 中的 rt_thread_delay 调用注释掉,那么线程 1、线程 2 根本没有执行的机会。
在实际产品中,我们不会让一个线程一直运行,而是使用"事件驱动"的方法让它运行:
- 线程要等待某个事件,事件发生后它才能运行
- 在等待事件过程中,它不消耗 CPU 资源
- 在等待事件的过程中,这个线程就处于挂起状态
在挂起状态的线程,它可以等待两种类型的事件:
- 时间相关的事件
- 可以等待一段时间:我等 2 分钟
- 也可以一直等待,直到某个绝对时间:我等到下午 3 点
- 同步事件:这事件由别的线程,或者是中断程序产生
- 例子 1:线程 A 等待线程 B 给它发送数据
- 例子 2:线程 A 等待用户按下按键
- 同步事件的来源有很多(这些概念在后面会细讲):
- 信号量(semaphores)
- 互斥量(mutexe)
- 事件集(event)
在等待一个同步事件时,可以加上超时时间。比如等待队里数据,超时时间设为
10ms:
- 10ms 之内有数据到来:成功返回
- 10ms 到了,还是没有数据:超时返回
4.5 关闭状态
当线程运行结束时,将处于关闭状态:
- 可由运行状态正常退出,进入关闭状态
- 或者通过线程删除函数进入关闭状态
rt_err_t rt_thread_detach()
,用来删除使用rt_thread_init()
创建的线程rt_err_t rt_thread_delete()
,用来删除使用rt_thread_create()
创建的线程
在进入关闭状态时,线程所占据的资源(比如栈)不会立即释放,需等到空闲进程运行时才能清理。