【STM32RT-Thread零基础入门】 7. 线程创建应用(多线程运行机制)
硬件:STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线
文章目录
- 前言
- 一、RT-Thread相关接口函数
- 1. 获取当前运行的线程
- 2. 设置调度器钩子函数
- 二、程序设计
- 1. 头文件包含及宏定义
- 2. 线程入口函数定义
- 3. main函数设计
- 三、程序测试
- 总结
前言
本章进一步研究多线程的运行机制。要求实现功能如下:创建2个线程,线程名称分别为LED和BEEP。两个线程的任务是连续5次打印本线程的名字后退出线程(注意:线程不执行控制LED和蜂鸣器动作)。
设计本任务的目的是观察LED和BEEP线程在操作系统中是如何同时运行的。
一、RT-Thread相关接口函数
1. 获取当前运行的线程
在程序的运行过程中,相同的一段代码可能会被多个线程执行,在执行的时候可以通过下面的函数接口获得当前执行的线程句柄:
rt_thread_t rt_thread_self(void);
该接口的返回值见下表:
返回 | 描述 |
---|---|
thread | 当前运行的线程句柄 |
返回 | RT_NULL:失败,调度器还未启动 |
2. 设置调度器钩子函数
在整个系统的运行时,系统都处于线程运行、中断触发和响应中断、切换到其他线程,甚至是线程间的切换过程中,或者说系统的上下文切换是系统中最普遍的事件。有时用户可能会想知道在一个时刻发生了什么样的线程切换,可以通过调用下面的函数接口设置一个相应的钩子函数。在系统线程切换时,这个钩子函数将被调用:
void rt_scheduler_sethook(void (*hook)(struct rt_thread* from, struct rt_thread* to));
设置调度器钩子函数的输入参数如下表所示:
参数 | 描述 |
---|---|
hook | 表示用户定义的钩子函数指针 |
钩子函数 hook() 的声明如下:
void hook(struct rt_thread* from, struct rt_thread* to);
调度器钩子函数 hook() 的输入参数如下表所示:
函数参数 | 描述 |
---|---|
from | 表示系统所要切换出的线程控制块指针 |
to | 表示系统所要切换到的线程控制块指针 |
注:请仔细编写你的钩子函数,稍有不慎将很可能导致整个系统运行不正常(在这个钩子函数中,基本上不允许调用系统 API,更不应该导致当前运行的上下文挂起)。
二、程序设计
使用rt_thread_t rt_thread_self()函数获取本线程的线程句柄,然后通过线程句柄,可以方便地获得线程地名称。对main.c进行如下程序设计
1. 头文件包含及宏定义
本任务代码中,我们使用预编译宏进行选择编译,使代码可以兼容两个版本,提高代码利用率
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
//#include "car_led.h" //包含LED控制模块头文件
//#include "car_beep.h" //包含蜂鸣器控制模块头文件#define THREAD_STACK_SIZE 1024 //定义线程栈大小
//两个线程优先级分别定义
#define THREAD_PRIORITY_LED 20
#define THREAD_PRIORITY_BEEP 20
#define THREAD_TIMESLICE 10 //定义线程时间片/*使用预编译宏进行选择编译,当定义以下宏时,讲开启调度器钩子功能*/
//#define SCHEDULER_HOOK#ifdef SCHEDULER_HOOK
//定义调度钩子函数
static void hook_of_scheduler(struct rt_thread* from,struct rt_thread* to)
{//打印调度信息:从一个线程切换到另一个线程运行rt_kprintf("from: %s --> to: %s \n",from->name,to->name);
}
#endif
2. 线程入口函数定义
本任务需要创建两个线程,所以要编写两个线程入口函数,分别为beep_thread_entry和led_thread_entry。
void beep_thread_entry(void * parameter)
{rt_thread_t tid;int count = 0; //打印出前5个调度过程while(1){tid =rt_thread_self(); //获取本线程地句柄//打印线程的名字和当前计数变量地值LOG_D("thread name: %s count = %d\n",tid->name,count);if (count++ ==5) //线程循环5次后退出 break; }//线程退出时打印退出信息LOG_D("thread %s exit\n",tid->name);
}void led_thread_entry()
{rt_thread_t tid;int count = 0; //打印出前5个调度过程while(1){tid =rt_thread_self(); //获取本线程地句柄//打印线程的名字和当前计数变量地值LOG_D("thread name: %s count = %d\n",tid->name,count);if (count++ ==5) //线程循环5次后退出 break; }//线程退出时打印退出信息LOG_D("thread %s exit\n",tid->name);
}
3. main函数设计
main只负责线程的创建,用动态的方法创建LED线程,静态方法创建beep线程。静态方法需要用户自定义线程栈空间和线程控制块。
/* 栈首地址必须系统对齐 */
ALIGN(RT_ALIGN_SIZE)
static char beep_stack[THREAD_STACK_SIZE]; //定义栈空间
static struct rt_thread beepThread; //静态方式定义beep线程控制块
rt_thread_t TidLed = RT_NULL; //动态方式定义LED线程句柄int main(void)
{int ret;#ifdef SCHEDULER_HOOK
//设置调度钩子rt_scheduler_sethook(hook_of_scheduler);#endif/* 动态方式创建线程 */TidLed = rt_thread_create("LED", led_thread_entry, RT_NULL,THREAD_STACK_SIZE, THREAD_PRIORITY_LED,THREAD_TIMESLICE);if (TidLed != RT_NULL)//判断线程是否成功创建rt_thread_startup(TidLed);//成功则启动线程else {//否则打印日志并即出LOG_D("can not create LED thread!");return -1;}/* 采用静态方式初始化线程 */ret = rt_thread_init(&beepThread,"BEEP",beep_thread_entry,RT_NULL,&beep_stack[0],sizeof(beep_stack),THREAD_PRIORITY_BEEP,THREAD_TIMESLICE);if (ret == RT_EOK) //判断线程是否成功创建rt_thread_startup(&beepThread); //成功则启动线程else { //否则打印日志并即出LOG_D("can not init beep thread!");return -1;}return RT_EOK;
}
三、程序测试
(1)使用终端连接开发板,然后按开发板reset键重启系统,终端调试信息如下图,发现两个线程轮流输出信息,可以间接说明两个线程时轮流执行的。
(2)把BEEP优先级改为19,修改后按照(1)进行测试,如图所示,即使LED线程先于BEEP线程创建,由于BEEP线程的优先级高于LED线程,因此BEEP线程被执行,LED要等BEEP执行完后再执行。
(3)打开预处理宏定义#define SCHEDULER_HOOK,把LED和BEEP优先级都设置为20,重新构建并下载。由下图可以看到,系统先运行main线程,再运行tshell线程,这是因为系统中main线程优先级默认为10,比tshell默认优先级20高,所以系统先运行main线程。
tshell运行后LED线程和BEEP线程接着轮流运行,由于这3个线程的优先级都是20,所以他们在属于同一个优先级的队列中,并且按启动先后顺序排列(注意:是启动顺序,即rt_thread_startup()函数的执行顺序,而不是创建程序),调度顺序也是按照启动的先后顺序进行的。
LED线程和BEEP线程退出后,进入tidle0线程运行,tidle0优先级在系统中最低,当所有高优先级的线程退出或者睡眠时,会进入tidle0线程运行。
(4)打开预处理宏定义#define SCHEDULER_HOOK,把LED和BEEP优先级分别设置为20、19。
修改后重新构建并下载程序,观察终端如图所示。系统运行顺序为:
main线程→BEEP线程→tshell线程→LED线程
总结
在操作系统中,所有线程各自独立运行,所有线程看起来是同时工作的,但在只有一个CPU核的情况下,在同一时刻只能有一个线程在CPU上运行,操作系统为每个线程分配一定的运行时间片,当线程的运行时间耗尽时,操作系统会调度下一个线程到CPU运行。由于时间片很小,使得我们觉得线程是在同时运行的。
相关文章:

【STM32RT-Thread零基础入门】 7. 线程创建应用(多线程运行机制)
硬件:STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线 文章目录 前言一、RT-Thread相关接口函数1. 获取当前运行的线程2. 设置调度器钩子函数 二、程序设计1. 头文件包含及宏定义2. 线程入口函数定义3. main函数设…...
.net日志系统
.NET 平台提供了强大的日志记录系统,用于在应用程序中记录各种事件、错误和调试信息。最常用的日志记录库是 Microsoft.Extensions.Logging,它是一个通用的日志接口和基础框架,可以与多种日志实现集成。以下是如何使用 .NET 日志系统的基本步骤: 安装 NuGet 包:首先,您需…...

SpringCloud学习笔记(二)_Eureka注册中心
一、Eureka简介 Eureka是一项基于REST(代表性状态转移)的服务,主要在AWS云中用于定位服务,以实现负载均衡和中间层服务器的故障转移。我们称此服务为Eureka Server。Eureka还带有一个基于Java的客户端组件Eureka Clientÿ…...
spark的eventLog日志分析
查找满足指定条件的app_id查询条件: 表名、时间、节点名时间限定: 最好适当放大, 不知道什么原因有点不准eventLog的存放路径: spark.history.fs.logDirectory 1. spark-sql 先限定时间段;数据是逐行读入的, 但 app_id要按整个文件过滤, 按每个条件打标;按app_id粒度聚合, 查…...

探究Java spring中jdk代理和cglib代理!
面对新鲜事物,我们要先了解在去探索事物的本质-默 目录 一.介绍二者代理模式 1.1.Jdk代理模式 1.2cglib代理模式 1.3二者区别 1.3.1有无接口 1.3.2灵活性 1.4对于两种代理模式的总结 1.4.1jdk代理模式 1.4.2cglib代理模式 二.两种代理模式应用场景 2.1jd…...
反转链表(C++)
1、迭代法的一种写法 ListNode* reverse_linkList(ListNode* head){if(head nullptr || head->next nullptr) return head;ListNode* begin nullptr;ListNode* mid head;ListNode* end head->next;while(true){mid->next begin;if(end nullptr){break;}begin …...
适配器模式:让不兼容的接口协同工作
在面向对象设计中,适配器模式是一种常见的结构型设计模式。它允许将不兼容的接口转换成客户端所期望的另一个接口,从而使不同的类协同工作。适配器模式的主要目的是解决不同接口之间的兼容性问题,同时也提高了代码的可重用性和灵活性。 问题…...
【1day】复现Milesight-VPNserver.js 任意文件读取漏洞
目录 一、漏洞描述 二、影响版本 三、资产测绘 四、漏洞复现 一、漏洞描述 Milesight路由器-VPN是由Milesight Technology Co., Ltd.开发的一种集成了VPN功能的路由器产品。它旨在为用户提供安全、可靠的远程访问和连接解决方案。Milesight-VPNserver.js存在任意文件读取…...
前端代码规范
1 husky husky用于绑定git hooks,在指定时机执行想要的命令 {"husky": {"hooks": {"pre-commit": "lint-staged" }} }需要手动修改.husky文件内容: . "$(dirname -- "$0")/_/husky.sh"n…...
Java接入文心一言
文章目录 文心一言应用创建接口对接接口文档代码示例依赖 常量类实体类 结束语 文心一言应用创建 首先需要先申请文心千帆大模型,申请地址:文心一言 (baidu.com),点击加入体验,等通过审核之后就可以进入文心千帆大模型后台进行应…...

信息管理系统三级等保的一些要求
一、前言 在做一些互联网系统或面向互联网的系统时,需要进行备案,需要满足网络信息安全维护规章及有关规章制度要求,才能发布到互联网。所以在做系统的需求分析时,往往需要把信息管理系统三级等保的需求加上,方便开发…...
第六届“蓝帽杯”电子取证模块(初赛)解析+全资源一次性分享
前言:资源一次性分享 手机+电脑+exe+内存四个模块,我自己在网上也找了很久,才把资源找齐全,题目我也整理在这里,方便大家训练。 目录...
《Go 语言第一课》课程学习笔记(九)
常量:Go 在“常量”设计上的创新有哪些? Go 语言在常量方面的创新包括下面这几点: 支持无类型常量;支持隐式自动转型;可用于实现枚举。 常量 Go 语言的常量是一种在源码编译期间被创建的语法元素。这是在说这个元素…...
docker 安装nginx 和 elasticsearch ik 自定义分词
1、切换到/mydata 文件夹 创建 nginx 目录 mkdir nginx 2、运行 docker run --name nginx -p 80:80 -d nginx:1.22.0 3、复制docker 里面的nginx配置到 外面的nginx/conf 下面 docker cp nginx:/etc/nginx /mydata/nginx 4、把 /mydata/nginx下面的nginx 改…...

谈谈收音机的发展
目录 1.什么是收音机 2.收音机的工作原理 3.收音机的发展历史 4.收音机的历史作用 1.什么是收音机 收音机是一种电子设备,用于接收和播放广播电台的无线电信号。它是人们获取各种音乐、新闻、娱乐和其他广播节目的常用设备。 收音机通常由以下几个部分组成&…...

QTreeWidget——信号处理
文章目录 基本属性信号一、信号种类二、信号测试1、currentItemChanged、itemCollapsed、itemExpanded三个信号的测试2、itemActivated信号3、 itemChanged信号4、其余信号的测试代码(包含以上代码) 基本属性 信号 一、信号种类 //当前项发生变化时触…...

【Java从入门到精通|1】从特点到第一个Hello World程序
写在前面 在计算机编程领域,Java是一门广泛应用的高级编程语言。它以其强大的跨平台性能、丰富的库和生态系统以及易于学习的语法而备受开发者欢迎。本文将引导您逐步了解Java的特点、如何安装和配置开发环境,以及如何编写您的第一个Java程序。 一、Java…...

JAVA 读取jar包中excel模板
1、在resources路径下,新建report文件夹,放入excel模板 2、配置文件中的目录,分隔符使用 / template: /report/报告模板V1.0.xlsx3、使用getResourceAsStream()读取 XSSFWorkbook wb;try {//需要以/开始InputStream resourceAsStream this.g…...

解决方案:fatal error: openssl/bio.h: 没有那个文件或目录
出现报错如下: 出现该错误的原因有两个: 没有安装openssl或者libssl-dev库Libssl-dev版本过高,需要降级 一. 没有安装openssl或者libssl-dev库 使用指令安装openssl: 我的是已经安装完成了,所以再把libssl-dev的库也…...

【MySQL系列】ALTER语句详解,以及UPDATE,DELECT,TRUNCATE语句的使用+区别
💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...