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

FreeRTOS【4】线程挂起和恢复

1.开发背景

       基于上一篇指引,成功创建并启动线程后,线程已经开始运行了,但是有时我们需要线程暂停运行,例如某个线程是控制 LED 闪灯的,如果现在需要让 LED 停止工作,单纯的关闭 LED 是没用的,因为下一时刻线程可能就会重新打开 LED 导致程序没有达到预期,所以线程引入了一个挂起的状态,如下图,FreeRTOS 引入了线程的多个状态。

        其中包含 Running、Ready、Suspended、Blocked 等状态。

Running:即运行态,是成功获取了 CPU 使用权的线程。

Ready:即准备态,单核 CPU 在同一时刻只能做一件事情,所以如果当前有其他线程获取了 CPU 的使用权(一般是高优先级线程),这个时候等待运行的线程就是准备态。

Suspended:即挂起态,正如上文说到的,如果我们想让某个线程暂时停止工作,就可以挂起对应线程,被挂起的线程就是挂起态。

Blocked:即阻塞态,因为线程存在高低优先级,如果高优先级线程一直运行会导致低优先级线程一直抢占不到 CPU 的使用权,如果使用挂起的方式去挂起高优先级线程,那么高优先级线程的实时性就会大打折扣,所以就引入了阻塞的概念,高优先级线程可以一直阻塞在某个事件,在阻塞期间会让出 CPU 的使用权,但是一旦高优先级线程满足指定事件就会立刻抢占低优先级线程的 CPU 使用权,这样就保证了高优先级线程的实时性。

        上述纯粹个人理解,如有误请见谅,可以参考链接:FreeRTOS task states and state transitions described

2.开发需求

        挂起、恢复和删除已有线程

3.开发环境

        window10 + MDK + STM32F429 + FreeRTOS10.3.1

4.实现步骤

4.1 线程挂起其他线程

1)创建控制线程和 2 个测试线程

/* 测试初始化 */
void aTest_Init(void)
{/* 创建动态任务 */xTaskCreate(TaskCtrl, "TaskCtrl", 500, NULL, 5, &p->taskCtrl);/* 共用一个任务函数 创建多个任务 */static char whichTask[TASK_LIST_SIZE][3] = {0};for (int i = 0; i < TASK_LIST_SIZE; i++){snprintf(whichTask[i], 2, "%d", i);xTaskCreate(TaskList, "TaskList", 500, (void*)whichTask[i], 5, &p->taskList[i]);}
}

2)测试线程循环打印

/* 动态任务组 */
static void TaskList(void *pvParameters)
{int count = 0;int whichTask = atoi(pvParameters);Log_Debug("%s [%d]\r\n", __func__, whichTask);for ( ; ; ){vTaskDelay(1000);Log_Debug("%s [%d] count = %d\r\n", __func__, whichTask, count++);}
}

3)控制线程间断挂起和恢复,使用 vTaskSuspend 挂起线程,vTaskResume 恢复线程。

/* 动态任务 */
static void TaskCtrl(void *pvParameters)
{Log_Debug("%s\r\n", __func__);/* 挂起线程 0 */vTaskDelay(3000);vTaskSuspend(p->taskList[0]);vTaskDelay(3000);vTaskResume(p->taskList[0]);for ( ; ; ){vTaskDelay(1000);}
}

4)测试结果

如图所示,可以看出线程0 中间有 3 个周期是停止工作的。

4.2 线程挂起线程本身

1)基于上面试验的基础上,修改测试线程为打印日志后挂起自身,这里做这个试验是为了验证 vTaskSuspend 如果传入参数为 NULL,即指向线程本身。

/* 动态任务组 */
static void TaskList(void *pvParameters)
{int count = 0;int whichTask = atoi(pvParameters);Log_Debug("%s [%d]\r\n", __func__, whichTask);for ( ; ; ){vTaskDelay(1000);Log_Debug("%s [%d] count = %d\r\n", __func__, whichTask, count++);vTaskSuspend(NULL);}
}

2)控制线程只需定期恢复线程即可

/* 动态任务 */
static void TaskCtrl(void *pvParameters)
{Log_Debug("%s\r\n", __func__);/* 挂起线程 0 */vTaskDelay(3000);vTaskResume(p->taskList[0]);for ( ; ; ){vTaskDelay(1000);}
}

3)测试结果

如图所示,测试线程执行了一次就挂起了本身,控制线程间隔 3s 之后唤醒了一次测试线程0

4)源码解析

vTaskSuspend

        -> prvGetTCBFromHandle

/** Several functions take an TaskHandle_t parameter that can optionally be NULL,* where NULL is used to indicate that the handle of the currently executing* task should be used in place of the parameter.  This macro simply checks to* see if the parameter is NULL and returns a pointer to the appropriate TCB.*/
#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) )

如源码所示,如果传入指针为空,即获取当前控制块 pxCurrentTCB

4.3 中断中恢复线程

1)基于上面的实验使用中断来代替控制线程来恢复已挂起的测试线程

/* Key2 PC13   Key0 PH3 Key1 PH2 */
void Exti13_TriggerInterrupt(void)
{if (mspGpio_GetInput("PC13") == 0){Log_Debug("%s 按键执行测试线程0 恢复\r\n", __func__);BaseType_t xYieldRequired;xYieldRequired = xTaskResumeFromISR(p->taskList[0]);portYIELD_FROM_ISR(xYieldRequired);mspExti_Close(13);}
}

在中断中使用 FreeRTOS 接口需要带 FromISR 后缀的,如 xTaskResumeFromISR,需要portYIELD_FROM_ISR 切换上下文,否则实时性会收到一定的影响,为了调试和演示方便在中断中打印了数据,在实际项目中切记不要在中断中停留,特别是打印等高延时操作。

2)测试结果

如图所示,在中断中恢复已经挂起的线程也是可以的。

4.4 线程删除

1)线程删除后会释放内存,由于现在的线程都是在系统堆栈动态开辟的,所以线程删除后内存会回归系统内存堆栈。

/* 动态任务 */
static void TaskCtrl(void *pvParameters)
{Log_Debug("%s\r\n", __func__);/* 挂起线程 0 */vTaskDelay(3000);Log_Info("FreeRTOS Remain Space = %d Bytes\r\n", xPortGetFreeHeapSize());vTaskDelete(p->taskList[0]);Log_Info("Delete Task0 And FreeRTOS Remain Space = %d Bytes\r\n", xPortGetFreeHeapSize());vTaskDelete(p->taskList[1]);Log_Info("Delete Task1 And FreeRTOS Remain Space = %d Bytes\r\n", xPortGetFreeHeapSize());for ( ; ; ){vTaskDelay(1000);
//        Log_Debug("%s\r\n", __func__);}
}

2)测试结果

如图所示,删除2个任务后,系统内存由 49472Bytes -> 51584Bytes -> 53696Bytes

相关文章:

FreeRTOS【4】线程挂起和恢复

1.开发背景 基于上一篇指引&#xff0c;成功创建并启动线程后&#xff0c;线程已经开始运行了&#xff0c;但是有时我们需要线程暂停运行&#xff0c;例如某个线程是控制 LED 闪灯的&#xff0c;如果现在需要让 LED 停止工作&#xff0c;单纯的关闭 LED 是没用的&#xff0c;因…...

CPU占用率过高排查

CPU占用率高是设备本身的一种现象&#xff0c;直观表现为display cpu-usage命令查询结果中整机CPU占用率“CPU usage”偏高&#xff0c;如超过70%。在网络运行中CPU高常常会导致其他业务异常&#xff0c;如BGP震荡、VRRP频繁切换、甚至设备无法登录。 通常&#xff0c;整机CPU占…...

关于 vs2019 c++20 规范里的 STL 库里模板 decay_t<T>

&#xff08;1&#xff09; 这个模板&#xff0c;在库代码里非常常见。 decay 英文是“衰弱&#xff0c;消减” 的意思&#xff0c;大概能感觉到就是要简化模板参数 T 的类型&#xff0c;去掉其上的修饰符。因为常用且复杂&#xff0c;故单独列出其源码和注释。先举例其应用场景…...

android C++打印堆栈

Android在Java层打印堆栈比较方便&#xff0c;代码如下&#xff1a; try {throw new Exception("Debug xxx call stack"); }catch(Exception e) {e.printStackTrace(); }但是在C模块中能打印调用堆栈吗&#xff1f;怎么打印调用栈呢&#xff1f; 答案是肯定的&…...

MySQL Undo Log、Redo Log、bin Log

Undo Log 回滚日志&#xff0c;用于将数据回滚到之前的状态。 MySQL在进行数据的增、删、改时&#xff0c;会将数据写入到Undo Log日志中。 对于Undo Log存在着insert和update两种类型的数据。插入语句对应的是insert类型&#xff0c;修改、删除语句对应的是update类型。 U…...

vld.ini配置文件说明

vld.ini配置文件说明 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Visual Leak Detector - 初始化/配置文件 ;; 版权所有 (c) 2005-2017 VLD团队 ;; ;; 本库是自由软件&#xff1b;你可以在自由软件基金会发布的GNU宽通用公共…...

NSS【web】刷题

[SWPUCTF 2021 新生赛]jicao 类型&#xff1a;PHP、代码审计、RCE 主要知识点&#xff1a;json_decode()函数 json_decode()&#xff1a;对JSON字符串解码&#xff0c;转换为php变量 用法&#xff1a; <?php $json {"ctf":"web","question"…...

将TailwindCSS默认单位rem转换为px

前言&#xff1a; 我这里需要将 默认的rem 转换为 px 原因是要使用 postcss-px-to-viewport 插件做移动端适配。 在tailwind.config.js文件中进行配置&#xff1a; 注意&#xff1a;这里 padding&#xff08;内边距&#xff09;、spacing&#xff08;外边距&#xff09;、width…...

命令模式(命令)

命令模式 文章目录 命令模式什么时命令模式通过示例了解命令模式 什么时命令模式 命令模式(Command),将一个请求封装为一个对象&#xff0c;从而使你可用不同的请求对客户进行参数化&#xff1a;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。 通过示例了解命令模…...

Android ashmem 原理分析

源码基于&#xff1a;Andoird U Kernel-5.10 0. 简介 ashmem 称为匿名共享内存(Anonymous Shared Memory)&#xff0c;它以驱动程序的形式实现在内核空间中。它有两个特点&#xff1a; 能否辅助内存管理系统来有效地管理不再使用的内存块(pin / unpin)&#xff1b; 通过Bind…...

redis报错500

之前自己举一反三把value也给序列化了&#xff1a; 然后报错了&#xff1a; 原因是这里传入的是Integer类型&#xff0c;序列化的话就变为string类型了...

GPT-3

论文&#xff1a;Language Models are Few-Shot Learners&#xff08;巨无霸OpenAI GPT3 2020&#xff09; 摘要 最近的工作表明&#xff0c;通过对大量文本进行预训练&#xff0c;然后对特定任务进行微调&#xff0c;在许多NLP任务和基准方面取得了实质性进展。虽然这种方法…...

MATLAB数组

文章目录 数组创建通过冒号创建一维数组通过logspace函数创建一维数组通过linspace函数创建一维数组 通过randperm生成随机整数排列运算算术运算关系运算逻辑运算优先顺序 矩阵创建矩阵操作下标引用矩阵信息提取删除与扩展合并矩阵元素的运算矩阵运算 数组 在MATLAB中一般使用…...

JAVA实验项目(二): 抽象类、接口的定义与使用

实验项目二 抽象类、接口的定义与使用 Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&…...

JVM内存模型最新面试题(持续更新)

问题&#xff1a;java中创建的对象一般放在哪里&#xff1f;(全流程包含从创建到回收) 回答 大部分对象在堆中&#xff0c;这个基本都知道&#xff1b; 少部分对象是会在栈中的&#xff0c;比如作用域不局限于方法内的方法内部变量&#xff0c;这类对象的特征一般就是生命周期…...

Nginx wss to ws 折腾记

jssip 或 sipml5 <----wss--->nginx<---ws---->fs(5066) fs_cli -x sofia loglevel all 9 日志如下&#xff1a; REGISTER sip:192.168.43.135 SIP/2.0 Via: SIP/2.0/WSS df7jal23ls0d.invalid;branchz9hG4bKurFnCK9qJuXQlSrbszSL1S6wbCokKlLr;rport From: <…...

Java入门基础学习笔记22——程序流程控制

程序流程控制&#xff1a;控制程序的执行顺序。 程序有哪些执行顺序&#xff1f; 顺序、分支和循环。 分支结构&#xff1a; if、switch 循环&#xff1a; for、while、do-while 顺序结构是程序中最简单最基本的流程控制&#xff0c;没有特定的语法结构&#xff0c;按照代码…...

java医院信息系统HIS源码SaaS模式Java版云HIS系统 接口技术RESTful API + WebSocket + WebService

java医院信息系统HIS源码SaaS模式Java版云HIS系统 接口技术RESTful API WebSocket WebService 云HIS是基于云计算的医疗卫生信息系统&#xff08;Cloud-Based Healthcare Information System&#xff09;&#xff0c;它运用云计算、大数据、物联网等新兴信息技术&#xff0c;…...

2024年成都高新区支持企业申报国家、省级、市级大数据产业发展、新一代信息技术与制造业融合发展、工业互联网推广应用等试点示范项目申报对象条件和奖补

一、申报对象 &#xff08;一&#xff09;本政策支持注册地址、税收关系在成都高新区&#xff0c;具有独立法人资格的企业。 &#xff08;二&#xff09;管理规范&#xff0c;无不良信用记录&#xff0c;自觉遵守安全生产、环境保护等方面的法律法规&#xff0c;近三年未发生…...

让《行列视》解放数据力量,提升业务洞察

在当今信息化浪潮下&#xff0c;数据已经成为企业发展的核心驱动力之一。如何更好地管理和利用数据&#xff0c;已成为企业发展过程中亟需解决的问题之一。而报表工具作为数据可视化和分析的利器&#xff0c;正逐渐受到企业的重视和青睐。 一、《行列视》作为报表工具的重要性…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

Qt Quick Controls模块功能及架构

Qt Quick Controls是Qt Quick的一个附加模块&#xff0c;提供了一套用于构建完整用户界面的UI控件。在Qt 6.0中&#xff0c;这个模块经历了重大重构和改进。 一、主要功能和特点 1. 架构重构 完全重写了底层架构&#xff0c;与Qt Quick更紧密集成 移除了对Qt Widgets的依赖&…...

C++中vector类型的介绍和使用

文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...

比特币:固若金汤的数字堡垒与它的四道防线

第一道防线&#xff1a;机密信函——无法破解的哈希加密 将每一笔比特币交易比作一封在堡垒内部传递的机密信函。 解释“哈希”&#xff08;Hashing&#xff09;就是一种军事级的加密术&#xff08;SHA-256&#xff09;&#xff0c;能将信函内容&#xff08;交易细节&#xf…...

Yolo11改进策略:Block改进|FCM,特征互补映射模块|AAAI 2025|即插即用

1 论文信息 FBRT-YOLO&#xff08;Faster and Better for Real-Time Aerial Image Detection&#xff09;是由北京理工大学团队提出的专用于航拍图像实时目标检测的创新框架&#xff0c;发表于AAAI 2025。论文针对航拍场景中小目标检测的核心难题展开研究&#xff0c;重点解决…...

视觉slam--框架

视觉里程计的框架 传感器 VO--front end VO的缺点 后端--back end 后端对什么数据进行优化 利用什么数据进行优化的 后端是怎么进行优化的 回环检测 建图 建图是指构建地图的过程。 构建的地图是点云地图还是什么信息的地图&#xff1f; 建图并没有一个固定的形式和算法…...

【Java基础】​​向上转型(Upcasting)和向下转型(Downcasting)

在面向对象编程中&#xff0c;转型&#xff08;Casting&#xff09; 是指改变对象的引用类型&#xff0c;主要涉及 继承关系 和 多态。 向上转型&#xff08;Upcasting&#xff09; ⬆️ 定义 将 子类对象 赋值给 父类引用&#xff08;自动完成&#xff0c;无需强制转换&…...