FreeRTOS ~(六)信号量 ~ (1/3)信号量解决同步缺陷

前情提要
FreeRTOS ~(四)同步互斥与通信 ~ (1/3)同步的缺陷
FreeRTOS ~(五)队列的常规使用 ~ (1/5)队列解决同步缺陷
举例子说明:利用信号量解决前述的"同步的缺陷"问题

使用信号量的时候记得包含相关头文件,及打开相关宏定义
#include "semphr.h"
#define  configUSE_COUNTING_SEMAPHORES   1
这里使用的例子仍和开篇两个链接的一致
/*-----------------------------------------------------------*/
static int sum = 0;					/* i自增,结果存放变量sum */
static volatile int flagCalcEnd = 0;/* 该Flag用于监测代码运行时长 */
static SemaphoreHandle_t xSemCalc;	/* 创建的计数型信号量的句柄 */
/*-----------------------------------------------------------*/
void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000000; i++)sum++;/* 上述计算完成,就将计数型信号量增加1 */xSemaphoreGive(xSemCalc);/* 当前代码是删除Task1,后面会讲到如果把这个删除了会怎么样?如何保证sum这个数值不被破坏? */vTaskDelete(NULL);}
}void Task2Function(void * param)
{while (1){/* flag用逻辑分析仪查看代码执行时长 */flagCalcEnd = 0;/* 阻塞时间设置为最大,要一直等待直到Task1计算完成,一旦能获取到信号量,就会退出阻塞态 */xSemaphoreTake(xSemCalc, portMAX_DELAY);flagCalcEnd = 1;printf("sum = %d\r\n", sum);}
}
/*-----------------------------------------------------------*/
int main( void )
{prvSetupHardware();/* 创建计数型信号量,最大计数值设置为10,初始计数值设置为0在本例程设计中Task1完成复杂的计算后,就give一次,也就是计数值会+1;只是验证信号量解决同步问题,这里设置为1也能满足需求,暂且定为10次,后续可以让Task1 give多次 */xSemCalc = xSemaphoreCreateCounting(10, 0);xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);vTaskStartScheduler();return 0;
}
Debug执行代码,用逻辑分析仪抓取flag变化,依此监测代码运行时长;
下图可以看到的是,代码只执行了3s就完成了计算,也就是在Task1进行计算的时候,Task2进入了阻塞态,没有参与调度,
因此3s可以认为是Task1独享CPU资源后,Task1完成计算的时间.
这就是我们想要的结果,解决了同步的缺陷.

在这里插入图片描述

上文中有提到,Task1执行完复杂的计算后就 vTaskDelete(NULL); 将自己删除掉了.
如果这里不删除掉会怎么样呢?
将上述代码中,仅修改这一个位置,修改如下:
void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000000; i++)sum++;xSemaphoreGive(xSemCalc);//vTaskDelete(NULL);}
}
运行观察会出现什么问题?
下图说明:Task1的计算完成了,并且将计数型信号量加1,但是打印的数据出现了问题;
也就是说没有计数型信号量做不到像队列那样可以将数据保存下来不被更改;
Task1计算完成for循环,give之后,此时如果Tick中断没有到呢?
那么Task1将继续执行for循环,sum将继续自增,因此我们要加上一些代码,实现想要的结果.

在这里插入图片描述

在上述代码的基础上,加入队列,以保证数据不被更改.
/*-----------------------------------------------------------*/
static int sum = 0;					/* i自增,结果存放变量sum */
static volatile int flagCalcEnd = 0;/* 该Flag用于监测代码运行时长 */
static SemaphoreHandle_t xSemCalc;	/* 创建的计数型信号量的句柄 */
static QueueHandle_t xQueueHandle1; /* 创建的队列的句柄 */
/*-----------------------------------------------------------*/
void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000000; i++)sum++;xSemaphoreGive(xSemCalc);/* 将计算完成的数据加入到队列中 */xQueueSend(xQueueHandle1,&sum,portMAX_DELAY);/* 这里同时将sum清零,验证上述的问题,这一步可做可不做 */sum = 0;}
}void Task2Function(void * param)
{int val = 0;while (1){flagCalcEnd = 0;xSemaphoreTake(xSemCalc, portMAX_DELAY);flagCalcEnd = 1;/* 队列接收数据,这里阻塞时间为0,因为代码能够执行到这里,说明队列中一定是有数据的,因此无需等待 */xQueueReceive(xQueueHandle1,&val,0);printf("sum = %d\r\n", val);}
}
/*-----------------------------------------------------------*/
int main( void )
{prvSetupHardware();/* 创建队列 */xQueueHandle1 = xQueueCreate(2,sizeof(int));if( xQueueHandle1 == NULL ){printf("can not create queue 1\r\n");}/* 创建计数型信号量 */xSemCalc = xSemaphoreCreateCounting(10, 0);xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);vTaskStartScheduler();return 0;
}
执行结果如下:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运动控制介绍

运动控制介绍 1 介绍1.1 概述1.2 运动控制的基本架构1.3 常见的控制功能1.4 运动控制研究的问题分类位置变化问题周期式旋转速度变化问题 1.5 知识体系1.6 路径规划 和 轨迹规划区别与联系1.7 运动控制系统 2 《运动控制系统》[班华 李长友 主编] 摘要1 绪论1.1 运动控制研究的…

【数据仓库】FineBI数据可视化使用体验

FineBI介绍 FineBI是新一代自助式BI工具,企业客户多,服务范围广.凭借finebi简单流畅的操作,强劲的大数据性能和自助式的分析体验。 1&#xff0c;对个人用户来说&#xff0c;免费的无限期试用&#xff0c;解锁所有功能&#xff0c;除了限制两个并发访问&#xff0c;个人用户可以…

CSS 两行文字两端对齐与字符间距的处理

前言 &#x1f44f;CSS 文字对齐与字符间距的处理&#xff0c;在这里&#xff0c;你可以了解到文字渐变&#xff0c;letter-spacing&#xff0c;text-align&#xff0c;text-align-last&#xff0c;filter等&#xff0c;速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关…

42. 会话划分问题

文章目录 题目需求思路一实现一题目来源 题目需求 现有页面浏览记录表&#xff08;page_view_events&#xff09;如下&#xff0c;每行数据代表&#xff1a;每个用户的每次页面访问记录。 规定若同一用户的相邻两次访问记录时间间隔小于60s&#xff0c;则认为两次浏览记录属于…

CSS 内容盒子与边框盒子

这篇比较重要&#xff0c;会不断更新❗ 文章目录 &#x1f447;内容盒子开发者工具的使用border 边框padding 内边距margin 外边距盒子整体尺寸元素默认样式与CSS重置元素分类块级标记行级标记行内块标记 display样式内容溢出裁剪掉溢出部分滚动条 圆角边框 border-radius ✌边…

学无止境·MySQL③

单表查询 题一创建表并插入数据薪水修改为5000将姓名为张三的员工薪水修改为3000元将姓名为李四的员工薪水修改为4000元&#xff0c;gener改为女 题一 1.创建表&#xff1a; 创建员工表employee&#xff0c;字段如下&#xff1a; id&#xff08;员工编号&#xff09;&#xff…

Day976.如何安全、快速地接入OAuth 2.0? -OAuth 2.0

如何安全、快速地接入OAuth 2.0&#xff1f; Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于如何安全、快速地接入OAuth 2.0&#xff1f;的内容。 授权服务将 OAuth 2.0 的复杂性都揽在了自己身上&#xff0c;这也是授权服务为什么是 OAuth 2.0 体系的核心的原因之…

c++读取字符串字符时出错

这是我做的一个c爬虫程序但是在抓取网页的时候string类型传递出现了问题 以下是图片代码 url的值是 "http://desk.zol.com.cn/" 我不知道为什么数据传递会出问题 请大佬指教 后面重新启动一遍编译器查一查断点有突然没问题了 &#xff0c;真是个玄学的问题。我还以…

mysql 大数据量从“.log“文件插入方法

要被插入的数据文件以及内容 表结构 插入成功&#xff0c;插入时如果有主键唯一索引&#xff0c;则按照唯一索引的大小顺序插入&#xff0c;速度会快很多

Linux——内核概念

一、内核 什么是内核&#xff1f; 计算机是由各种外部硬件设备组成的&#xff0c;如内存、cpu、硬盘等。如果每个应用都要和这些硬件对接通信协议&#xff0c;就太麻烦了&#xff0c;所以这个工作就由内核来负责。内核作为软件连接硬件设备的桥梁&#xff0c;使应用开发者只需…

(ARM)7/5

1.串口发送单个字符 2.串口发送字符串 uart4.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_uart.h" #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h"//初始化相关操作 void hal_uart4_init();//发送一个字符 v…

云计算——虚拟化层架构

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 前言 本章将会讲解云计算的虚拟化层架构&#xff0c;了解云计算虚拟化层都有哪些架构模式…