当前位置: 首页 > news >正文

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_initrt_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 slicetick 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() 创建的线程

在进入关闭状态时,线程所占据的资源(比如栈)不会立即释放,需等到空闲进程运行时才能清理。

4.6 完整的状态转换图

在这里插入图片描述

相关文章:

  • 论文检测器
  • 2025年全国I卷数学压轴题解答
  • 邮科ODM摄像头:多维度护航高铁安全系统方案解析
  • leetcode刷题日记——岛屿数量
  • 结构化文件管理实战:实现目录自动创建与归类
  • [软件测试]:什么软件测试?如何设计测试用例?
  • IPMB:智能平台管理总线——数据中心硬件的“神经系统”详解
  • [USACO23FEB] Bakery S
  • (37)课56--??:建立保存点 SAVEPOINT spA,回滚(至保存点) ROLLBACK (to spA)及综合举例。
  • 从落水到快速救援:北斗如何打通生命通道
  • CSS 布局指南
  • MyBatis————进阶
  • Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目
  • 大模型链路调试平台之LangSmith实战指南
  • codeforces C. Cool Partition
  • Python爬虫(四):PyQuery 框架
  • Kafka入门-监控与可视化
  • sshd代码修改banner
  • 在 Figma 中导出图标为 Iconfont
  • 认识CMake并使用CMake构建自己的第一个项目
  • 建网站是怎么造成的/合肥最新消息今天
  • 美女做基网站/襄阳网站seo
  • bootstrap个人网站模板/徐州百度seo排名
  • 网站做镜像的有什么用/客服外包
  • 嘉善网站建设jswebs/长尾关键词挖掘工具
  • 网站排名如何稳定/如何宣传自己的网站