【FreeRTOS】同步互斥与通信 有缺陷的同步示例

目录

  • 1 同步互斥与通信
    • 1.1 同步互斥与通信概述
    • 1.2 同步与互斥的概念
    • 1.3 同步的例子:有缺陷
    • 1.4 freertos.c源码
      • 3. 互斥的例子:有缺陷
      • 4. 通信的例子:有缺陷
      • 5. FreeRTOS的解决方案


1 同步互斥与通信

1.1 同步互斥与通信概述

参考《FreeRTOS入门与工程实践(基于DshanMCU-103)》里《第10章 同步互斥与通信》

本章是概述性的内容。可以把多任务系统当做一个团队,里面的每一个任务就相当于团队里的一个人。团队成员之间要协调工作进度(同步)、争用会议室(互斥)、沟通(通信)。多任务系统中所涉及的概念,都可以在现实生活中找到例子。

各类RTOS都会涉及这些概念:任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)等。我们先站在更高角度来讲解这些概念。

1.2 同步与互斥的概念

一句话理解同步与互斥:我等你用完厕所,我再用厕所。

什么叫同步?就是:哎哎哎,我正在用厕所,你等会。 什么叫互斥?就是:哎哎哎,我正在用厕所,你不能进来。

同步与互斥经常放在一起讲,是因为它们之的关系很大,“互斥”操作可以使用“同步”来实现。我“等”你用完厕所,我再用厕所。这不就是用“同步”来实现“互斥”吗?

再举一个例子。在团队活动里,同事A先写完报表,经理B才能拿去向领导汇报。经理B必须等同事A完成报表,AB之间有依赖,B必须放慢脚步,被称为同步。在团队活动中,同事A已经使用会议室了,经理B也想使用,即使经理B是领导,他也得等着,这就叫互斥。经理B跟同事A说:你用完会议室就提醒我。这就是使用"同步"来实现"互斥"。

有时候看代码更容易理解,伪代码如下:

 void  抢厕所(void){if (有人在用) 我眯一会;用厕所;喂,醒醒,有人要用厕所吗;}

1.3 同步的例子:有缺陷

程序:在06_create_task_use_params的基础上,修改出12_task_sync_exclusion

创建两个任务:一个用来执行大量的计算任务,另一个永年执行打印函数显示OLED

xTaskCreate(                //加返回值是 判断任务有没有创建成功CalcTask,           //计算任务"Task1",            //声音任务128,                //栈大小NULL,       //传入的参数 g_Task1InfoosPriorityNormal,   //优先级默认NULL                //任务句柄 无);
xTaskCreate(                //加返回值是 判断任务有没有创建成功LcdPrintTask,       //LCD打印任务"Task1",            //声音任务128,                //栈大小&g_Task2Info,       //传入的参数 g_Task1InfoosPriorityNormal,   //优先级默认NULL                //任务句柄 无);

编写这两个函数

static struct TaskPrintInfo g_Task1Info = {0, 0, "Task1"};  // (0,0),Task1
static struct TaskPrintInfo g_Task2Info = {0, 3, "Task2"};  // (0,3),Task2
static struct TaskPrintInfo g_Task3Info = {0, 6, "Task3"};  // (0,6),Task3static int g_LCDCanUse = 1; //默认=1 能使用LCD
uint32_t g_sum = 0;   //定义一个计数值
static volatile int g_calc_end = 0; // 计算结束标志位,使用volatile不要用编译器优化优化uint64_t g_time = 0;void CalcTask(void *params)
{uint32_t i = 0;     //定义一个计数值g_time = system_get_ns();   //获取当前系统时间for (i = 0; i < 10000000; i ++){g_sum += i;}g_calc_end = 1; //计算完成标志位   置位g_time = system_get_ns() - g_time;  //运行这段任务消耗的时间vTaskDelete(NULL);  //计算完成杀死任务
}void LcdPrintTask(void *params)
{int len;while (1){LCD_PrintString(0, 0, "Waiting");// vTaskDelay(3000);while (g_calc_end == 0);    //等待/* 打印信息 */if (g_LCDCanUse){g_LCDCanUse = 0;LCD_ClearLine(0, 0);len = LCD_PrintString(0, 0, "Sum: ");LCD_PrintHex(len, 0, g_sum, 1);LCD_ClearLine(0, 2);len = LCD_PrintString(0, 2, "Time(ms): ");LCD_PrintSignedVal(len, 2, g_time/1000000); //打印消耗了多长时间g_LCDCanUse = 1;}vTaskDelete(NULL);  //任务自杀}
}

这里遇到了bug!!!

在这里插入图片描述

  • 程序卡死在while循环里了,但是这个变量已经是1了,为什么程序会卡死在上面两行汇编语句呢???

  • 原因是编译器优化了我们的变量

对这个变量,执行while (g_calc_end == 0); 这条语句的时候,它会读取内存,把变量的值放到CPU某个寄存器里,以后一直就判断那个寄存器,但是这个寄存器得到的是原始的值,并没有每次都去读取内存

  • 这个变量是在其他任务里被修改的,那我们使用这个变量的时候,每次都需要读内存!
  • 那怎么办呢??我们加上一个volatile就可以了

烧录代码运行
在这里插入图片描述

计算10000000个数需要2.5S,真的是这样的吗???

我们现在有两个任务

在这里插入图片描述
他们是怎么调度的呢?
两个任务优先级相同,A任务运行1ms,B任务运行1ms,A任务运行1ms,B任务运行1ms

在这里插入图片描述

任务B执行的程序是死等,这也耗费了一半的时间,在任务B的开始就等待3S左右

vTaskDelay(3000);   //开始的时候我先等待3000Tick

加上这句等待3S之后,就变成了1278ms

在这里插入图片描述

确实如此

这个死循环可以用其他方法来代替

  • 等任务A计算完成之后,用任务A唤醒任务B
  • 使用同步的时候,我们需要考虑如何提高处理器的性能!
  • 让等待的任务阻塞,不参与CPU的调度!

这节课学习了同步的例子,有缺陷的例子

学习视频:【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 【精准空降到 13:38】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=25&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=818

1.4 freertos.c源码

/* USER CODE BEGIN Header */
#include "driver_led.h"
#include "driver_lcd.h"
#include "driver_mpu6050.h"
#include "driver_timer.h"
#include "driver_ds18b20.h"
#include "driver_dht11.h"
#include "driver_active_buzzer.h"
#include "driver_passive_buzzer.h"
#include "driver_color_led.h"
#include "driver_ir_receiver.h"
#include "driver_ir_sender.h"
#include "driver_light_sensor.h"
#include "driver_ir_obstacle.h"
#include "driver_ultrasonic_sr04.h"
#include "driver_spiflash_w25q64.h"
#include "driver_rotary_encoder.h"
#include "driver_motor.h"
#include "driver_key.h"
#include "driver_uart.h"
#include "music.h"/********************************************************************************* File Name          : freertos.c* Description        : Code for freertos applications******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */BaseType_t ret; // long
static TaskHandle_t xSoundTaskHandle;           // void *  在全局变量里记录句柄static StackType_t g_pucStackOfLightTask[128];  // 变量前缀的意思是 全局变量g 指针p uint8_t类型uc的StackOfLightTask 光任务的栈
StaticTask_t g_TCBofLightTask;                  // 光任务的TCB
static TaskHandle_t xLightTaskHandle;           // void *  在全局变量里记录句柄static StackType_t g_pucStackOfColorTask[128];  // 变量前缀的意思是 全局变量g 指针p uint8_t类型uc的StackOfLightTask 色任务的栈
StaticTask_t g_TCBofColorTask;                  // 色任务的TCB
static TaskHandle_t xColorTaskHandle;           // void *  在全局变量里记录句柄/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {.name = "defaultTask",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityNormal,
};/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */struct TaskPrintInfo{uint8_t x;      //定义坐标xuint8_t y;      //定义坐标ychar name[16];  //定义要打印输出的内容,最多显示16个字符
};static struct TaskPrintInfo g_Task1Info = {0, 0, "Task1"};  // (0,0),Task1
static struct TaskPrintInfo g_Task2Info = {0, 3, "Task2"};  // (0,3),Task2
static struct TaskPrintInfo g_Task3Info = {0, 6, "Task3"};  // (0,6),Task3static int g_LCDCanUse = 1; //默认=1 能使用LCD
uint32_t g_sum = 0;   //定义一个计数值
static volatile int g_calc_end = 0; // 计算结束标志位,使用volatile不要用编译器优化优化uint64_t g_time = 0;void CalcTask(void *params)
{uint32_t i = 0;     //定义一个计数值g_time = system_get_ns();   //获取当前系统时间for (i = 0; i < 10000000; i ++){g_sum += i;}g_calc_end = 1; //计算完成标志位   置位g_time = system_get_ns() - g_time;  //运行这段任务消耗的时间vTaskDelete(NULL);  //计算完成杀死任务
}void LcdPrintTask(void *params)
{int len;while (1){LCD_PrintString(0, 0, "Waiting");vTaskDelay(3000);   //开始的时候我先等待3000Tickwhile (g_calc_end == 0);    //等待/* 打印信息 */if (g_LCDCanUse){g_LCDCanUse = 0;LCD_ClearLine(0, 0);len = LCD_PrintString(0, 0, "Sum: ");LCD_PrintHex(len, 0, g_sum, 1);LCD_ClearLine(0, 2);len = LCD_PrintString(0, 2, "Time(ms): ");LCD_PrintSignedVal(len, 2, g_time/1000000); //打印消耗了多长时间g_LCDCanUse = 1;}vTaskDelete(NULL);  //任务自杀}
}
/* USER CODE END FunctionPrototypes */void StartDefaultTask(void *argument);void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) *//*** @brief  FreeRTOS initialization* @param  None* @retval None*/
void MX_FREERTOS_Init(void) {/* USER CODE BEGIN Init */LCD_Init();LCD_Clear();/* USER CODE END Init *//* USER CODE BEGIN RTOS_MUTEX *//* add mutexes, ... *//* USER CODE END RTOS_MUTEX *//* USER CODE BEGIN RTOS_SEMAPHORES *//* add semaphores, ... *//* USER CODE END RTOS_SEMAPHORES *//* USER CODE BEGIN RTOS_TIMERS *//* start timers, add new ones, ... *//* USER CODE END RTOS_TIMERS *//* USER CODE BEGIN RTOS_QUEUES *//* add queues, ... *//* USER CODE END RTOS_QUEUES *//* Create the thread(s) *//* creation of defaultTask *///  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... *///  /* 创建任务:声 */
//  // 先创建一个动态分配内存的任务
//  ret = xTaskCreate(                //加返回值是 判断任务有没有创建成功
//            PlayMusic,          //孤勇者的函数
//            "SoundTask",        //声音任务
//            128,                //栈大小
//            NULL,               //无传入的参数
//            osPriorityNormal,   //优先级默认
//            & xSoundTaskHandle  //任务句柄
//            );//  
//  /* 创建任务:光 */ 
//  // 创建一个静态分配内存的任务
//  xLightTaskHandle = xTaskCreateStatic(
//            Led_Test,           //LED测试函数,PC13以500ms间隔亮灭一次
//            "LightTask",        //光任务
//            128,                //栈大小,这里提供了栈的大小(长度)
//            NULL,               //无传入的参数
//            osPriorityNormal,   //优先级默认
//            g_pucStackOfLightTask,  // 静态分配的栈,一个buffer,这里只提供了首地址,长度就是栈的大小,最开始栈的类型不对,栈的类型uint32_t
//            &g_TCBofLightTask       // 取址TCB
//  );
//  
//  /* 创建任务:色 */ 
//  xColorTaskHandle = xTaskCreateStatic(
//            ColorLED_Test,           //LED测试函数,PC13以500ms间隔亮灭一次
//            "ColorTask",        //光任务
//            128,                //栈大小,这里提供了栈的大小(长度)
//            NULL,               //无传入的参数
//            osPriorityNormal,   //优先级默认
//            g_pucStackOfColorTask,  // 静态分配的栈,一个buffer,这里只提供了首地址,长度就是栈的大小
//            &g_TCBofColorTask       // 取址TCB
//  );xTaskCreate(                //加返回值是 判断任务有没有创建成功CalcTask,           //计算任务"Task1",            //声音任务128,                //栈大小NULL,       //传入的参数 g_Task1InfoosPriorityNormal,   //优先级默认NULL                //任务句柄 无);xTaskCreate(                //加返回值是 判断任务有没有创建成功LcdPrintTask,       //LCD打印任务"Task1",            //声音任务128,                //栈大小&g_Task2Info,       //传入的参数 g_Task1InfoosPriorityNormal,   //优先级默认NULL                //任务句柄 无);/* USER CODE END RTOS_THREADS *//* USER CODE BEGIN RTOS_EVENTS *//* add events, ... *//* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask *//*** @brief  Function implementing the defaultTask thread.* @param  argument: Not used* @retval None*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */LCD_Init();LCD_Clear();for(;;){//Led_Test();//LCD_Test();//MPU6050_Test(); //DS18B20_Test();//DHT11_Test();//ActiveBuzzer_Test();//PassiveBuzzer_Test();//ColorLED_Test();IRReceiver_Test();  //影//IRSender_Test();//LightSensor_Test();//IRObstacle_Test();//SR04_Test();//W25Q64_Test();//RotaryEncoder_Test();//Motor_Test();//Key_Test();//UART_Test();}/* USER CODE END StartDefaultTask */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application *//* USER CODE END Application */

在这里插入图片描述

3. 互斥的例子:有缺陷

讲解这个程序"06_create_task_use_params"的互斥缺陷。

4. 通信的例子:有缺陷

5. FreeRTOS的解决方案

  • 正确性

  • 效率:等待者要进入阻塞状态

  • 多种解决方案

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

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

相关文章

ESP32CAM物联网教学08

ESP32CAM物联网教学08 本地网页控制小车 小智制作的物联网小车,在与云台监控摄像头的PK中,一路攻城掠地、勇往直前。突然有一天,他觉得似乎忘了最开始时的初衷,忘了一路走来的首发站:这个不就是一辆遥控车吗?我能不能就做一辆快乐的、纯粹的遥控车。 CameraWebServer转换…

揭秘数据之美:【Seaborn】在现代【数学建模】中的革命性应用

目录 已知数据集 tips 生成数据集并保存为CSV文件 数据预览&#xff1a; 导入和预览数据 步骤1&#xff1a;绘制散点图&#xff08;Scatter Plot&#xff09; 步骤2&#xff1a;添加回归线&#xff08;Regression Analysis&#xff09; 步骤3&#xff1a;分类变量分析&…

深度学习——深度学习中感受野的计算

感受野 在卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;感受野&#xff08;Receptive Field&#xff09; 是一个非常重要的概念。它描述了网络中某一层的输出&#xff08;通常是特征图上的一个像素点&#xff09;所对应的输入图像上的空间范围。这个范围代表了该输出…

更新GCC版本问题处理(Could not resolve host: mirrorlist.centos.org;)更换SCL配置源/SCL后yum使用不了

SCL&#xff1a; 在 Linux 系统中&#xff0c;更新 GCC&#xff08;GNU Compiler Collection&#xff09;编译器需要使用 Software Collections (SCL) 库的原因主要有以下几点&#xff1a; https://wiki.centos.org/AdditionalResources/Repositories/SCLhttps://wiki.centos…

【C++】 解决 C++ 语言报错:未定义行为(Undefined Behavior)

文章目录 引言 未定义行为&#xff08;Undefined Behavior, UB&#xff09;是 C 编程中非常危险且难以调试的错误之一。未定义行为发生时&#xff0c;程序可能表现出不可预测的行为&#xff0c;导致程序崩溃、安全漏洞甚至硬件损坏。本文将深入探讨未定义行为的成因、检测方法…

【LLM大模型】LangChain从到入门到实战

1.概述 最近&#xff0c;在研究LangChain时&#xff0c;发现一些比较有意思的点&#xff0c;今天笔者将给大家分享关于LangChain的一些内容。 2.内容 2.1 什么是LangChain&#xff1f; LangChain是一项旨在赋能开发人员利用语言模型构建端到端应用程序的强大框架。它的设计…

电子元器件基础知识总结

1.0 电阻 电阻的定义&#xff1a;导体对电流的阻碍作用称之为电阻【每一种导体都有内阻的存在】 闭合的电路中电子的移动输出有多快&#xff1f;电子在导体中的移动速度是很慢的 【铜线中电流的移动速度】 电子受到原子核的束缚&#xff0c;移动的速度很慢&#xff0c;在电压足…

WPS图片无法居中、居中按钮无法点击(是灰色的)

在PPT中复制对象到WPS word中后&#xff0c;导致图片一直靠左&#xff0c;而无法居中 直接选中图片是错误的&#xff1a; 这时你会发现居中按钮无法点击&#xff08;是灰色的&#xff09; 正确的是选中图片的前面的部分&#xff0c;然后点击居中&#xff0c;或者Ctrl E

【网络安全】第4讲 身份认证技术(笔记)

一、身份认证技术概述 1、身份认证 是网络安全的第一道防线。是最基本的安全服务&#xff0c;其他的安全服务都依赖于它。在物联网应用系统中&#xff0c;身份认证也是整个物联网应用层信息安全体系的基础。 2、基本身份认证技术 &#xff08;1&#xff09;双方认证 是一种双…

收银系统源码-营销活动-幸运抽奖

1. 功能描述 营运抽奖&#xff1a;智慧新零售收银系统&#xff0c;线上商城营销插件&#xff0c;商户/门店在小程序商城上设置抽奖活动&#xff0c;中奖人员可内定&#xff1b; 2.适用场景 新店开业、门店周年庆、节假日等特定时间促销&#xff1b;会员拉新&#xff0c;需会…

亚信安全:《2024云安全技术发展白皮书》

标签 云计算 安全威胁 云安全技术 网络攻击 数据保护 一句话总结 《云安全技术发展白皮书》全面分析了云计算安全威胁的演进&#xff0c;探讨了云安全技术的发展历程、当前应用和未来趋势&#xff0c;强调了构建全面云安全防护体系的重要性。 摘要 云安全威胁演进&#xff…

Android14之获取包名/类名/服务名(二百二十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Python酷库之旅-第三方库Pandas(003)

目录 一、用法精讲 4、pandas.read_csv函数 4-1、语法 4-2、参数 4-3、功能 4-4、返回值 4-5、说明 4-6、用法 4-6-1、创建csv文件 4-6-2、代码示例 4-6-3、结果输出 二、推荐阅读 1、Python筑基之旅 2、Python函数之旅 3、Python算法之旅 4、Python魔法之旅 …

react 项目中预防xss攻击的插件 dompurify

一、安装 $ yarn add dompurify $ yarn add --dev types/dompurify 二、使用 import DOMPurify from dompurify;// 1、处理&#xff1a; DOMPurify.sanitize(htmlContent)// 2、之后放进 dangerouslySetInnerHTML dangerouslySetInnerHTML{{ __html: cleanHTML }} 如&#…

nftables(1)基本原理

简介 nftables 是 Linux 内核中用于数据包分类的现代框架&#xff0c;用来替代旧的 iptables&#xff08;包括 ip6tables, arptables, ebtables 等&#xff0c;统称为 xtables&#xff09;架构。nftables 提供了更强大、更灵活以及更易于管理的规则集配置方式&#xff0c;使得…

没有网络安全就没有信息的未来!

&#x1f447;&#x1f447;&#x1f447; 如果你也想学习:黑客&网络安全的零基础攻防教程 今天只要你给我的文章点赞&#xff0c;我私藏的网安学习资料一样免费共享给你们&#xff0c;来看看有哪些东西。 在这里领取&#xff1a; 这个是我花了几天几夜自整理的最新最全…

CFS三层内网渗透——外网打点(一)

目录 外网打点 先爆破一下看看有没有啥可进攻路径 尝试那个可疑的路径发现是thinkphp这个框架&#xff0c;同时也知道了版本&#xff0c;那就nday打吧 写入php ​编辑写入php成功&#xff0c;简简单单nday拿下​编辑 蚁剑rce尝试链接 打点成功 外网打点 先爆破一下看看有…

Windows 上帝模式是什么?开启之后有什么用处?

Windows 上帝模式是什么 什么是上帝模式&#xff1f;Windows 上帝模式&#xff08;God Mode&#xff09;是一个隐藏的文件夹&#xff0c;通过启用它&#xff0c;用户可以在一个界面中访问操作系统的所有管理工具和设置选项。这个功能最早出现在 Windows Vista 中&#xff0c;并…

七月开刷|50天吃透660+880‼️

现在只刷一本题集根本不够 去做做24年的考研真题卷就什么都明白了&#xff0c;24年的卷子就是典型的知识点多&#xff0c;杂&#xff0c;计算量大。 而现在市面上的任何一本题集&#xff0c;都无法做到包含所有的知识点&#xff0c;毕竟版面有限&#xff01; 所以&#xff0…

HY Lisp 读取宏(reader macro)学习

在学习HY lisp语言的时候HY编程快速入门实践课第三章 HY宏入门-CSDN博客&#xff0c;学习到了读取宏&#xff08;reader macro&#xff09;&#xff0c;尝试将其概念弄明白。 首先&#xff0c;读取宏是Lisp语言中都有的一种概念&#xff0c;所以可以通过任意一种Lisp语言的文档…