【FreeRTOS】同步与互斥通信-有缺陷的互斥案例
目录
- 同步与互斥通信
- 同步与互斥的概念
- 同步与互斥并不简单
- 缺陷
- 分析汇编指令
- 优化过程 - 关闭中断
- 时间轴分析
- 思考时刻
参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》
同步与互斥通信
同步与互斥的概念
一句话理解同步与互斥:我等你用完厕所,我再用厕所。
什么叫同步?就是:哎哎哎,我正在用厕所,你等会。 什么叫互斥?就是:哎哎哎,我正在用厕所,你不能进来。
同步与互斥经常放在一起讲,是因为它们之的关系很大,“互斥”操作可以使用“同步”来实现。我“等”你用完厕所,我再用厕所。这不就是用“同步”来实现“互斥”吗?
再举一个例子。在团队活动里,同事A先写完报表,经理B才能拿去向领导汇报。经理B必须等同事A完成报表,AB之间有依赖,B必须放慢脚步,被称为同步。在团队活动中,同事A已经使用会议室了,经理B也想使用,即使经理B是领导,他也得等着,这就叫互斥。经理B跟同事A说:你用完会议室就提醒我。这就是使用"同步"来实现"互斥"。
有时候看代码更容易理解,伪代码如下:
void 抢厕所(void){if (有人在用) 我眯一会;用厕所;喂,醒醒,有人要用厕所吗;}
假设有A、B两人早起抢厕所,A先行一步占用了;B慢了一步,于是就眯一会;当A用完后叫醒B,B也就愉快地上厕所了。
在这个过程中,A、B是互斥地访问“厕所”,“厕所”被称之为临界资源。我们使用了“休眠-唤醒”的同步机制实现了“临界资源”的“互斥访问”。
同一时间只能有一个人使用的资源,被称为临界资源。比如任务A、B都要使用串口来打印,串口就是临界资源。如果A、B同时使用串口,那么打印出来的信息就是A、B混杂,无法分辨。所以使用串口时,应该是这样:A用完,B再用;B用完,A再用。
同步与互斥并不简单
假设两个任务都是OLED显示的操作,底层都是IIC代码
假设任务A发送了起始信号,此时发生一次调度,任务B也发送了起始信号
这样IIC时序就被打乱了,IIC时序烂七八糟的
- 所以使用红框的代码,必须互斥的访问
假设A运行到108行,被切换出去了(全局变量还没有清零),运行任务B,任务B能正常运行,假设任务B运行到一半被切换到任务A,任务A才开始清零,A也可以使用LCD
如果切换任务发生在108行这一点上,那么A和B都可以同时使用LCD,但是这个概率非常非常低,基本不会在这里产生Tick中断
- 大部分时间,我们使用全局变量保护这个LCD是没有问题的,但是也有缺陷!~
- 程序运行很长时间,就有可能发送这样的缺陷!
缺陷
这个缺陷问题在于判断这个变量和设置这个变量被打断了!
在裸机程序里,可以使用一个全局变量或静态变量实现互斥操作,比如要互斥地使用LCD,可以使用如下代码:
int LCD_PrintString(int x, int y, char *str) {static int bCanUse = 1;if (bCanUse){ bCanUse = 0;/* 使用LCD */bCanUse = 1;return 0;}return -1;}
但是在RTOS里,使用上述代码实现互斥操作时,大概率是没问题的,但是无法确保万无一失。
假设如下场景:有两个任务A、B都想调用LCD_PrintString,任务A执行到第4行代码时发现bCanUse为1,可以进入if语句块,它还没执行第6句指令就被切换出去了;然后任务B也调用LCD_PrintString,任务B执行到第4行代码时也发现bCanUse为1,也可以进入if语句块使用LCD。在这种情况下,使用静态变量并不能实现互斥操作。
上述例子中,是因为第4、第6两条指令被打断了,那么如下改进:在函数入口处先然让bCanUse减一。这能否实现万无一失的互斥操作呢?
int LCD_PrintString(int x, int y, char *str) {static int bCanUse = 1;bCanUse--;if (bCanUse == 0){ /* 使用LCD */bCanUse++;return 0;}else{bCanUse++;return -1;}}
进入这个函数,bCanUse变量就减减,初值=1,减减后等于0
然后判断这个变量是否等于0,然后就执行下面的语句了
如果任务A能够执行完这个自减语句,才发生调度,执行任务B,在任务B里bCanUse先减减,减完就变成-1了,无法使用LCD
分析汇编指令
把bCanuse指令拆分一下,把第4行的代码使用汇编指令表示如下:
04.1 LDR R0, [bCanUse] // 读取bCanUse的值,存入寄存器R004.2 DEC R0, #1 // 把R0的值减一04.3 STR R0, [bCanUse] // 把R0写入变量bCanUse
假设如下场景:有两个任务A、B都想调用LCD_PrintString,任务A执行到第04.1行代码时读到的bCanUse为1,存入寄存器R0就被切换出去了;然后任务B也调用LCD_PrintString,任务B执行到第4行时发现bCanUse为1并把它减为0,执行到第5行代码时发现条件成立可以进入if语句块使用LCD,然后任务B也被切换出去了;现在任务A继续运行第04.2行代码时R0为1,运行到第04.3行代码时把bCanUse设置为0,后续也能成功进入if的语句块。在这种情况下,任务A、B都能使用LCD。
优化过程 - 关闭中断
上述方法不能保证万无一失的原因在于:**在判断过程中,被打断了。**如果能保证这个过程不被打断,就可以了:通过关闭中断来实现。
示例1的代码改进如下:在第5~7行前关闭中断。
没有办法产生Tick中断,无法调度,所以就不会冲突了~
int LCD_PrintString(int x, int y, char *str) {static int bCanUse = 1;disable_irq();if (bCanUse){ bCanUse = 0;enable_irq();/* 使用LCD */bCanUse = 1;return 0;}enable_irq();return -1;}
示例2的代码改进如下:在第5行前关闭中断。
int LCD_PrintString(int x, int y, char *str) {static int bCanUse = 1;disable_irq();bCanUse--;enable_irq();if (bCanUse == 0){ /* 使用LCD */bCanUse++;return 0;}else{disable_irq();bCanUse++;enable_irq();return -1;}}
时间轴分析
现在任务A和任务B都使用同一个函数,都想打印自己的信息
看时间轴,B在不断地判断,不断地失败,耗费了CPU资源
这种使用全局变量和关闭中断的方法,可以实现打印,但是太消耗CPU资源
那我们能不能实现A打印的过程中,B再来访问,B就阻塞,等待A执行完,用A唤醒B? 如下图所示:
思考时刻
- 后面用信号量和互斥量完成这些目标
相关文章:

【FreeRTOS】同步与互斥通信-有缺陷的互斥案例
目录 同步与互斥通信同步与互斥的概念同步与互斥并不简单缺陷分析汇编指令优化过程 - 关闭中断时间轴分析 思考时刻 参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》 同步与互斥通信 同步与互斥的概念 一句话理解同步与互斥:我等你用完厕所,我再…...
Docker 安装 Python
Docker 安装 Python 在当今的软件开发领域,Docker 已成为一项关键技术,它允许开发人员将应用程序及其依赖环境打包到一个可移植的容器中。Python,作为一种广泛使用的高级编程语言,经常被部署在 Docker 容器中。本文将详细介绍如何在 Docker 中安装 Python,以及如何配置环…...

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——4.预后相关外泌体基因确定单因素cox回归(2)
内容如下: 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…...
C++: Map数组的遍历
在C中,map是一个关联容器,它存储的元素是键值对(key-value pairs),其中每个键都是唯一的,并且自动根据键来排序。遍历map的方式有几种,但最常用的两种是使用迭代器(iterator…...

【Windows】Bootstrap Studio(网页设计)软件介绍及安装步骤
软件介绍 Bootstrap Studio 是一款专为前端开发者设计的强大工具,主要用于快速创建现代化的响应式网页和网站。以下是它的主要特点和功能: 直观的界面设计 Bootstrap Studio 提供了直观的用户界面,使用户能够轻松拖放元素来构建网页。界面…...
二维舵机颜色追踪,使用树莓派+opencv+usb摄像头+两个舵机实现颜色追踪,采用pid调控
效果演示 二维云台颜色追踪 使用树莓派opencvusb摄像头两个舵机实现颜色追踪,采用pid调控 import cv2 import time import numpy as np from threading import Thread from servo import Servo from pid import PID# 初始化伺服电机 pan Servo(pin19) tilt Serv…...

c进阶篇(四):内存函数
内存函数以字节为单位更改 1.memcpy memcpy 是 C/C 中的一个标准库函数,用于内存拷贝操作。它的原型通常定义在 <cstring> 头文件中,其作用是将一块内存中的数据复制到另一块内存中。 函数原型:void *memcpy(void *dest, const void…...

新手入门:无服务器函数和FaaS简介
无服务器(Serverless)架构的价值在于其成本效益、弹性和扩展性、简化的开发和部署流程、高可用性和可靠性以及使开发者能够专注于业务逻辑。通过自动化资源调配和按需计费,无服务器架构能够降低成本并适应流量变化,同时简化开发流…...

基于Transformer的端到端的目标检测 | 读论文
本文正在参加 人工智能创作者扶持计划 提及到计算机视觉的目标检测,我们一般会最先想到卷积神经网络(CNN),因为这算是目标检测领域的开山之作了,在很长的一段时间里人们都折服于卷积神经网络在图像处理领域的优势&…...

6.8应用进程跨网络通信
《计算机网络》第7版,谢希仁 理解socket通信...
redis布隆过滤器原理及应用场景
目录 原理 应用场景 优点 缺点 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用位数组和哈希函数来判断一个元素是否存在于集合中。 原理 数据结构: 位数组:一个由0和1组成的数组,初始…...

vue+openlayers之几何图形交互绘制基础与实践
文章目录 1.实现效果2.实现步骤3.示例页面代码3.基本几何图形绘制的关键代码 1.实现效果 绘制点、线、多边形、圆、正方形、长方形 2.实现步骤 引用openlayers开发库。加载天地图wmts瓦片地图。在页面上添加几何图形绘制的功能按钮,使用下拉列表(sel…...

「多模态大模型」解读 | 突破单一文本模态局限
编者按:理想状况下,世界上的万事万物都能以文字的形式呈现,如此一来,我们似乎仅凭大语言模型(LLMs)就能完成所有任务。然而,理想很丰满,现实很骨感——数据形态远不止文字一种&#…...

Redis深度解析:核心数据类型与键操作全攻略
文章目录 前言redis数据类型string1. 设置单个字符串数据2.设置多个字符串类型的数据3.字符串拼接值4.根据键获取字符串的值5.根据多个键获取多个值6.自增自减7.获取字符串的长度8.比特流操作key操作a.查找键b.设置键值的过期时间c.查看键的有效期d.设置key的有效期e.判断键是否…...

C语言 指针和数组——指针的算术运算
目录 指针的算术运算 指针加上一个整数 指针减去一个整数 指针相减 指针的关系比较运算 小结 指针的算术运算 指针加上一个整数 指针减去一个整数 指针相减 指针的关系比较运算 小结 指针变量 – 指针类型的变量,保存地址型数据 指针变量与其他类型…...

[C++][CMake][CMake基础]详细讲解
目录 1.CMake简介2.大小写?3.注释1.注释行2.注释块 4.日志 1.CMake简介 CMake是一个项目构建工具,并且是跨平台的 问题 – 解决 如果自己动手写Makefile,会发现,Makefile通常依赖于当前的编译平台,而且编写Makefile的…...
CCD技术指标
CCD尺寸,即摄象机靶面。原多为1/2英寸,现在1/3英寸的已普及化,1/4英寸和1/5英寸也已商品化。CCD像素,是决定了显示图像的清晰程度,。CCD是由面阵感光元素组成,每一个元素称为像素,像素越多&…...

SpringBoot系列——使用Spring Cache和Redis实现查询数据缓存
文章目录 1. 前言2. 缓存2.1 什么是缓存2.2 使用缓存的好处2.3 缓存的成本2.4 Spring Cache和Redis的优点 3. Spring Cache基础知识3.1 Spring Cache的核心概念3.2 Spring Cache的注解3.2.1 SpEL表达式3.2.2 Cacheable3.2.3 CachePut3.2.4 CacheEvict 4. 实现查询数据缓存4.1 准…...

【算法】(C语言):冒泡排序、选择排序、插入排序
冒泡排序 从第一个数据开始到第n-1个数据,依次和后面一个数据两两比较,数值小的在前。最终,最后一个数据(第n个数据)为最大值。从第一个数据开始到第n-2个数据,依次和后面一个数据两两比较,数值…...
iOS项目怎样进行二进制重排
什么是二进制重排 ? 在iOS项目中,二进制重排(Binary Reordering 或者 Binary Rearrangement)是一种优化技术,主要目的是通过重新组织应用程序的二进制文件中的代码和数据段,来提高应用程序的性能ÿ…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...