RT-Thread 线程管理(二)

news2025/1/18 4:45:42

系统线程

系统线程是指由系统创建的线程,用户线程是由用户程序调用线程管理接口创建的线程,在 RT-Thread 内核中的系统线程有空闲线程和主线程。

空闲线程

空闲线程(idle)是系统创建的最低优先级的线程,线程状态永远为就绪态。当系统中无其他就绪线程存在时,调度器将调度到空闲线程,它通常是一个死循环,且永远不能被挂起。

在某线程运行完毕,系统将自动删除线程:自动执行rt_thread_exit()函数,先将该线程从系统就绪队列中删除,再将该线程的状态更改为关闭状态,不再参与系统调度,然后挂入rt_thread_defunct僵尸队列(资源未回收、处于关闭状态的线程队列)中,最后空闲线程会回收被删除线程的资源。

空闲线程也提供了接口来运行用户设置的钩子函数,在空闲线程运行时会调用钩子函数,适合处理功耗管理、看门狗喂狗等工作。空闲线程必须有得到执行的机会,即其它线程不允许一直while(1)死卡,必须调用具有阻塞性质的函数;否则例如线程删除、回收等操作将无法得到正确执行。

主线程

在系统启动时,系统会创建main线程,它的入口函数为main_thread_entry(),用户的应用入口函数main()就是从这里真正开始的,系统调度器启动后,main 线程就开始运行,过程如下图,用户可以在 main() 函数里添加自己的应用程序初始化代码。

在这里插入图片描述

线程的管理方式

在这里插入图片描述
包含:创建 / 初始化线程、启动线程、运行线程、删除 / 脱离线程。
可以使用rt_thread_create()创建一个动态线程,使用rt_thread_init()初始化一个静态线程,动态线程与静态线程的区别是:

  1. 动态线程是系统自动从动态内存堆上分配栈空间与线程句柄(初始化heap之后才能使用create创建线程)。
  2. 静态线程是由用户分配栈空间与线程句柄。

创建和删除线程

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);

调用这个函数时,系统会从动态堆内存中分配一个线程句柄以及按照参数中指定的栈大小从动态堆内存中分配相应的空间。分配出来的栈空间是按照 rtconfig.h 中配置的 RT_ALIGN_SIZE 方式对齐。

在这里插入图片描述
对于一些使用 rt_thread_create() 创建出来的线程,当不需要使用,或者运行出错时,我们可以使用下面的函数接口来从系统中把线程完全删除掉:

rt_err_t rt_thread_delete(rt_thread_t thread);

调用该函数后,线程对象将会被移出线程队列并且从内核对象管理器中删除,线程占用的堆栈空间也会被释放,收回的空间将重新用于其它的内存分配。
rt_thread_delete()函数删除线程接口,仅仅是把线程的状态更改为RT_THREAD_CLOSE状态,放入到rt_thread_defunct队列中;而真正的删除动作(释放线程控制块和释放线程栈)需要到下一次执行空闲线程时,由空闲线程完成最后的线程删除动作。

在这里插入图片描述
注:rt_thread_create() 和 rt_thread_delete() 函数仅在使能了系统动态堆时才有效(即 RT_USING_HEAP 宏定义已经定义了)。

初始化和脱离线程

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);

静态线程的线程句柄(或者说线程控制块指针)、线程栈由用户提供。
静态线程是指线程控制块、线程运行栈一般都设置为全局变量,在编译时就被确定、被分配处理,内核不负责动态分配内存空间。
用户提供的栈首地址需要做系统对齐(例如ARM上需要做4字节对齐)。

在这里插入图片描述

对于用 rt_thread_init() 初始化的线程,使用 rt_thread_detach() 将使线程对象在线程队列和内核对象管理器中被脱离。线程脱离函数如下:

rt_err_t rt_thread_detach (rt_thread_t thread);

在这里插入图片描述
这个函数接口是和 rt_thread_delete() 函数相对应的, rt_thread_delete() 函数操作的对象是 rt_thread_create() 创建的句柄,而 rt_thread_detach() 函数操作的对象是使用 rt_thread_init() 函数初始化的线程控制块。同样,线程本身不应调用这个接口脱离线程本身。

启动线程

创建(初始化)的线程状态处于初始状态,并未进入就绪线程的调度队列,我们可以在线程初始化 / 创建成功后调用下面的函数接口让该线程进入就绪态:

rt_err_t rt_thread_startup(rt_thread_t thread);

当调用这个函数时,将把线程的状态更改为就绪状态,并放到相应优先级队列中等待调度。如果新启动的线程优先级比当前线程优先级高,将立刻切换到这个线程。线程启动接口 rt_thread_startup() 的参数和返回值见下表:

在这里插入图片描述

获得当前线程

在程序的运行过程中,相同的一段代码可能会被多个线程执行,在执行的时候可以通过下面的函数接口获得当前执行的线程句柄:

rt_thread_t rt_thread_self(void);

在这里插入图片描述

使线程让出处理器资源

当前线程的时间片用完或者该线程主动要求让出处理器资源时,它将不再占有处理器,调度器会选择相同优先级的下一个线程执行。
线程调用这个接口后,这个线程仍然在就绪队列中。

rt_err_t rt_thread_yield(void);

调用该函数后,当前线程首先把自己从它所在的就绪优先级线程队列中删除,然后把自己挂到这个优先级队列链表的尾部,然后激活调度器进行上下文切换(如果当前优先级只有这一个线程,则这个线程继续执行,不进行上下文切换动作)。

rt_thread_yield()函数和rt_schedule()函数比较相像,但在有相同优先级的其它就绪线程存在时,系统的行为却完全不一样。

  • 执行 rt_thread_yield() 函数后,当前线程被换出,相同优先级的下一个就绪线程将被执行。
  • 而执行 rt_schedule() 函数后,当前线程并不一定被换出,即使换出,也不会放到就绪线程链表的尾部,而是在系统中选取就绪的优先级最高的线程执行(如果系统中没有比当前线程优先级更高的线程存在,那么执行完 rt_schedule() 函数后,系统将继续执行当前线程)。

使线程睡眠

在实际应用中,我们有时需要让运行的当前线程延迟一段时间,在指定的时间到达后重新运行,这就叫做 “线程睡眠”。线程睡眠可使用以下三个函数接口:

rt_err_t rt_thread_sleep(rt_tick_t tick);
rt_err_t rt_thread_delay(rt_tick_t tick);
rt_err_t rt_thread_mdelay(rt_int32_t ms);

这三个函数接口的作用相同,调用它们可以使当前线程挂起一段指定的时间,当这个时间过后,线程会被唤醒并再次进入就绪状态。这个函数接受一个参数,该参数指定了线程的休眠时间。线程睡眠接口 rt_thread_sleep/delay/mdelay() 的参数和返回值见下表:

在这里插入图片描述

挂起和恢复线程

当线程调用 rt_thread_delay() 时,线程将主动挂起;当调用 rt_sem_take(),rt_mb_recv() 等函数时,资源不可使用也将导致线程挂起。处于挂起状态的线程,如果其等待的资源超时(超过其设定的等待时间),那么该线程将不再等待这些资源,并返回到就绪状态;或者,当其他线程释放掉该线程所等待的资源时,该线程也会返回到就绪状态。

线程挂起使用下面的函数接口:

rt_err_t rt_thread_suspend (rt_thread_t thread);

在这里插入图片描述
Note
一个线程尝试挂起另一个线程是一个非常危险的行为,因此RT-Thread对此函数有严格的使用限制:该函数只能自己挂起自己,不可以在线程A中尝试挂起线程B。
而且在挂起线程自己后,需要立刻调用rt_schedule()函数进行手动的线程上下文切换。

这是因为A线程在尝试挂起B线程时,A线程并不清楚B线程正在运行什么程序,一旦B线程正在使用例如互斥量、信号量等影响、阻塞其他线程(如C线程)的内核对象,如果此时其他线程也在等待这个内核对象,那么A线程尝试挂起B线程的操作将会引发其他线程(如C线程)的饥饿,严重危及系统的实时性。

恢复线程就是让挂起的线程重新进入就绪状态,并将线程放入系统的就绪队列中;如果被恢复线程在所有就绪态线程中,位于最高优先级链表的第一位,那么系统将进行线程上下文的切换。线程恢复使用下面的函数接口:

rt_err_t rt_thread_resume (rt_thread_t thread);

在这里插入图片描述

控制线程

当需要对线程进行一些其他控制时,例如动态更改线程的优先级,可以调用如下函数接口:

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() 或 rt_thread_detach() 函数调用。

设置和删除空闲钩子

空闲钩子函数是空闲线程的钩子函数,如果设置了空闲钩子函数,就可以在系统执行空闲线程时,自动执行空闲钩子函数来做一些其他事情,比如系统指示灯。设置/删除空闲钩子的接口如下:

rt_err_t rt_thread_idle_sethook(void (*hook)(void));
rt_err_t rt_thread_idle_delhook(void (*hook)(void));

Note
空闲线程是一个线程状态永远为就绪态的线程,因此设置的钩子函数必须保证空闲线程在任何时刻都不会处于挂起状态,例如rt_thread_delay(),rt_sem_take()等可能会导致线程挂起的函数都不能使用。
由于malloc、free等内存相关的函数内部使用了信号量作为临界区保护,因此在钩子函数内部也不允许调用。

设置调度器钩子

在整个系统的运行时,系统都处于线程运行、中断触发-响应中断、切换到其他线程,系统的上下文切换是系统中最普遍的事件。
有时用户可能会想知道在一个时刻发生了什么样的线程切换,可以通过调用下面的函数接口设置一个相应的钩子函数。在系统线程切换时,这个钩子函数将被调用:

void rt_scheduler_sethook(void (*hook)(struct rt_thread* from, struct rt_thread* to));

在这里插入图片描述
仔细编写钩子函数,稍有不慎导致整个系统运行不正常(在这个钩子函数中,基本上不允许调用系统API,更不应该导致当前运行的上下文挂起)

关于删除线程:大多数线程是循环执行的,无需删除;而能运行完毕的线程,RTT在线程运行完毕后,自动删除线程,在rt_thread_exit()里完成删除动作。
用户只需要了解该接口的作用,不推荐使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/960213.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决D盘的类型不是基本,而是动态的问题

一、正确的图片 1.1图片 1.2本人遇到的问题 二、将动态磁盘 转为基本盘 2.1 基本概念,动态无法转化为基本,不是双向的,借助软件 网址:转换动态磁盘到普通磁盘_检测到计算机本地磁盘为动态分区_卫水金波的博客-CSDN博客 2.2分区…

每日一题 2240. 买钢笔和铅笔的方案数

难度:中等 枚举就行 class Solution:def waysToBuyPensPencils(self, total: int, cost1: int, cost2: int) -> int:res 0for i in range(total//cost1 1):res (total - i * cost1) // cost2res 1return res

高等数学啃书汇总重难点(四)不定积分

本章主要考察方法性的技巧,对于某些理论性的概念,建议在练习中加强理解,不定积分的要义在于不断练习、不断拓宽眼界 一.不定积分的概念 二.原函数存在定理 三.不定积分的定义 四.基本积分表 五.不定积分的性质 六.一类换元法 七.二类换元法 八…

phpstorm配置链接sqlserver数据库

开启sqlserver的TCP/IP 1433端口

硬件性能评估指标-DMIPS、MFLOPS、MAC、TOPS

硬件性能评估指标-DMIPS、MFLOPS、MAC、TOPS DMIPS(Dhrystone Million Instructions Per Second): DMIPS用于衡量计算机系统的整体指令执行性能,通常关注整数操作。它基于Dhrystone基准测试来计算,该测试主要包含整数运…

如何利用好 IntelliJ IDEA 的调试功能辅助代码调试

文章目录 调试的重要性配置断点启动调试利用 IntelliJ IDEA 的调试功能观察变量和表达式单步执行和逐级跳转查看调用栈条件断点监视变量远程调试使用断点条件和日志 调试最佳实践总结 🎉欢迎来到Java学习路线专栏~如何利用好 IntelliJ IDEA 的调试功能辅助代码调试 …

YOLOv8超参数调优教程! 使用Ray Tune进行高效的超参数调优!

原创文章为博主个人所有,未经授权不得转载、摘编、倒卖、洗稿或利用其它方式使用上述作品。违反上述声明者,本站将追求其相关法律责任。 这篇博文带大家玩点新的东西,也是一直以来困扰大家最大的问题—超参数调优! 之前的 YOLOv5 我使用遗传算法做过很多次调优,实验一跑就…

实战项目 在线学院之集成springsecurity

一 操作配置 1.0 工程结构 1.1 在common下创建spring_security模块 1.2 pom文件中依赖的注入 1.3 在service_acl模块服务中引入spring-security权限认证模块 1.3.1 service_acl引入spring-security 1.3.2 在service_acl编写查询数据库信息 定义userDetailServiceImpl 查询用…

day08-领取优惠券(高并发优化:超卖、锁失效、事务边界、事务失效)

由于优惠券的发放数量限制、每人限领数量限制,因此在领取优惠券的过程中必须判断优惠券的库存以及当前用户的领取数量。也就是避免出现超发现象,这跟电商中的库存超卖是处理是类似的。 通过今天的学习,希望大家可以达成下列目标:…

【网络教程】群晖如何正确的安装openwrt旁路由

文章目录 准备安装导入镜像创建虚拟机访问旁路由旁路由网络设置准备 我这里的环境是群晖DSM7.2版本首先大家需要预先安装套件Virtual Machine Manager,这里就省略了 根据个人需求去下载openwrt的固件,下载的时候选择x86的img镜像文件,这里也可以直接使用我使用的这个固件(资…

ArcGIS土地利用程度综合指数分析

成图展示: 土地利用程度综合指数 第一步 准备数据 使用的数据为2010年河南省土地利用类型数据与其行政区划县级数据(为了节省操作,这里使用上次实验的部分数据[1],各土地利用类型已被提取) 第二步 面积统计 水域为例…

倍量阳线后缩倍阴选股公式,识别短期行情拐点

成交量(VOL)是指一段时间内成交的总手数,反映了资金的流入和流出,是判断市场走势的重要指标,为分析主力行为提供了重要参考。本文结合价格和成交量,编写倍量阳线后缩倍阴选股公式。 在技术分析中,可以利用成交量来衡量…

叮!你的 AI安全“秘籍”已送达,请签收

2023年初,全球生成式 AI 产业迎来了爆发式增长,大量AI产品和应用纷纷落地,让用户深度感知AI的魅力。预计到2032年,生成式AI市场的营收规模将从2022年的400亿美元增长至1.3万亿美元。 就在大量用户“尝鲜”生成式 AI 时&#xff0…

湖南省天农农家食品以“数”驱变,产销一体升级,探索食品供应链新模式|亿发

2023年,数字化技术作为重塑食品行业重要的力量,正以不可逆转的趋势改变着企业经营的方式。食品行业如何把握机遇,才能在时代竞争中探索新 生? 于逆势,择同行。本文以天农农家食品为实例,阐述传统产供销食品企业的数字…

查找(考研数据结构)

一、二叉查找树(BST) 1、BST的性质 【2011统考】下列关键字序列,不可能构成某二叉排序树中一条查找路径的是(A) A、95,22,91,24,94,71 B、92&#x…

Uncaught ReferenceError: process is not defined

最近在搞老项目升级,将Vue2.6.11里的vuecli5.0.8升级到vite最新版本4.4.9,中间遇到不少问题,有机会以后做记录。 遇到问题 把所有的工作就搞好项目也成功的跑起来,页面一片空白。打开控制台 Uncaught ReferenceError: process is not defi…

Git小白入门——上手实操之创建仓库和代码提交

版本库 什么是版本库呢?版本库又名仓库,英文名repository,简单理解成一个目录,目录里的所有文件都可以被Git管理,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或…

MySQL索引和查询优化

文章目录 1.Mysql索引2. b- tree 与 b tree3.覆盖索引和回表查询4.查询优化1.Explain 5.优化实战举例**用户搜索****订单查询****分页查询** 1.Mysql索引 MySQL索引是一种用于提高数据库查询效率的数据结构。它可以加快数据检索的速度,减少查询所需的IO操作和计算…

ChatGPT完成Excel公式计算业绩提成

公司业绩提成是一种激励措施,通常是指根据公司的业绩表现,对员工的绩效进行评估,然后给予相应的奖励或提成。 这种激励措施可以鼓励员工努力工作,提高团队的竞争力和生产效率,从而推动公司的业绩增长。 不过具体的提成计算方式和金额是根据公司政策和个人表现而定的。 例如…