【ESP-IDF FreeRTOS】信号量

news2024/9/21 22:24:18

下一个内容,信号量。

先包含头文件。

#include "freertos/semphr.h"

我们通过队列可以进行任务间的数据传递,也可以通过队列来控制任务间的同步。如果我只需要控制任务而不需要传递数据,那么我们完全可以用信号量来代替队列。

简单介绍一下信号量,它约等于是没有容量的队列,或者把它当成是一个计数器。我们对信号量的操作有加一和减一。

如果信号量当前的值为0并且我需要进行减一操作,那么会阻塞,直到其他任务对信号量进行加一操作。由此我们可以发现信号量和队列的共性,信号量的加一操作就类似于队列的塞数据,信号量的减一操作就类似于队列的取数据,区别在于信号量没存数据,我们只是用它控制任务的同步。

可能有小伙伴会好奇,我直接整个全局变量去控制不就好了嘛,为什么非得用信号量。

全局变量来当信号量看起来好像没什么问题,但是确实是有问题的。

我们使用全局变量++或是全局变量--的时候,我们敲的代码是一行的,但是我们的代码终究是要转成汇编代码然后再转成机器码的,转成汇编代码的时候,我们原本一行代码的全局变量++就会变成三行,如果我这三行代码执行到一半的时候,任务切换了,导致我们未能成功将全局变量++,往小了说影响我们功能的实现,往大了说可能直接导致程序报错。

而我们的信号量属于原子操作,原子操作顾名思义就类似于原子,不能再分割了(也许吧,这属于物理范畴我不了解),也就是说当我们进行原子操作的时候,一定会执行完再切换任务,我们对信号量的操作要么一点不动,要么一定完成。

信号量我们先介绍两种,一种是计数信号量,一种是二进制信号量。差别就在于计数信号量的上限我们可以指定(最多计到多少数),而二进制信号量的上限就是1,因此二进制信号量只有两种状态,一个是空另一个是满。

我们使用下面这个宏去创建计数信号量。

xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)

传入的是信号量最大值和初始值。

我们再细看一下这个宏可以发现,本质上创建信号量和创建队列是一样的。

所以理论上我们拿一个队列句柄类型的变量去接收创建得到的信号量也是可以的,并且队列集中也是可以存放信号量的,因为它们根本就是一个东西嘛。

但我们最好还是用SemaphoreHandle_t这个来接收信号量,这样见名知意。

删除信号量使用下面这个宏。

vSemaphoreDelete(xSemaphore)

宏里调用的也是删除队列的函数。

那剩下就是对信号量进行加一或减一操作了,或者换个大家比较熟悉的说法——PV操作。

先是加一操作,这里有个更形象的名字,give给也就是加一。

xSemaphoreGive(xSemaphore)

接下来是减一操作。名字也很形象,take拿也就是减一。

xSemaphoreTake(xSemaphore, xBlockTime)

上面无论是take还是give都有中断版本,也就是在函数后面加上FromISR,这边就不再介绍了。

另外还要提一下信号量和队列的另一个差别,那就是通用是塞东西,如果队列满了暂时塞不了,那么会有等待时间(进入阻塞),等待塞进去。而信号量加一超过了上限,那么直接返回错误,而不会进入阻塞等待能够加一。

信号量比较简单就不演示了,下一个介绍一下互斥锁。

头文件跟信号量是同一个,它们也有很多相似的地方,使用起来基本一样,创建——加一(上锁)——减一(开锁)——销毁。

甚至在一般情况下,我们可以把互斥锁看作是二进制信号量来使用。

互斥锁的开锁(give)和上锁(take)以及销毁用的函数都和信号量是一样的,因此我们在额外介绍一下互斥锁的创建以及互斥锁和二进制信号量的差别即可。

下面这个宏创建互斥锁。

xSemaphoreCreateMutex()

从下面这个官方示例可以得知互斥锁的句柄类型和信号量是一样的。

点开这个宏往上翻一翻可以发现,互斥锁,信号量,队列本质上是一个玩意,不过在创建的时候给我们默认加了个参数来指定类型为互斥锁,差别就在这边了。

在应用层上我们有个规定,那就是谁上锁谁开锁,而不能任务A上锁,然后让任务B开锁,这也是互斥锁和信号量的差别。

信号量更适用于控制数量有多个的资源,而互斥锁更适用于控制只有一个的核心资源。

假设我们只有一个核心资源,而任务A和任务B都需要用到,我们使用互斥锁来保证核心资源的使用,那么我们应该在任务中做到在使用前上锁,保证在使用过程中不会被其他任务干扰,在使用后开锁,保证之后能让其他任务使用。

互斥锁在使用上take和give是在同一个任务中使用的,而我们之前使用信号量,基本上take和give是分任务使用的。

一个简单的小例子大家就懂了。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
SemaphoreHandle_t mutex;

void test1(void*) {
    while (1) {
        xSemaphoreTake(mutex,0);
        // 假设在这边使用核心资源
        xSemaphoreGive(mutex);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void test2(void*) {
    while (1) {
        xSemaphoreTake(mutex,0);
        // 假设在这边使用核心资源
        xSemaphoreGive(mutex);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_main(void) {
    TaskHandle_t test1_handle;
    TickType_t curTime = xTaskGetTickCount();
    mutex = xSemaphoreCreateMutex();
    xTaskCreate(test1, "test1", 1024 * 2, NULL, configMAX_PRIORITIES /2,&test1_handle);
    xTaskCreate(test2, "test2", 1024 * 2, NULL, configMAX_PRIORITIES /2,NULL);
    while (1) {
        xTaskDelayUntil(&curTime, pdMS_TO_TICKS(1000));
    }
}

那么大家或许会有疑问,既然信号量和互斥锁是一个东西(句柄类型一样,不过其实在底层有不同),只是在使用层面上不同,那为什么要有互斥锁呢?我用二进制信号量貌似也能代替互斥锁。

那我们想象一个场景,我们创建了很多优先级不一样的任务,优先级高的任务更容易拿到时间片,也就更容易执行,如果高优先级任务和低优先级任务共用一个核心资源,并且我们使用二进制信号量来控制,当低优先级任务使用核心资源的时候,将信号量减一,高优先级任务想要使用这个核心资源的时候就只能等着,但是由于低优先级任务执行的概率比较低,因此尽管高优先级的优先级比较高,但还是得不到执行,这就导致了“饥饿”现象。

而互斥锁有个优先级继承机制,当高优先级任务请求一个被低优先级任务持有的互斥锁时,系统会将持有锁的低优先级任务的优先级临时提升到与高优先级任务相同的级别。这样,持有锁的任务能够更快地完成其执行并释放锁,从而使高优先级任务能够继续执行。

这就是互斥锁和信号量在本质上的差别。

互斥锁更适合用于需要严格互斥访问共享资源的场景。由于具有优先级继承机制,它能够在一定程度上减少因任务优先级不同而导致的死锁或延迟问题。

而二进制信号量更适用于任务间或任务与中断间的同步。例如,当一个中断发生时,可以通过释放一个二进制信号量来通知一个或多个任务进行相应处理。由于二进制信号量相对简单且没有优先级继承机制的开销,因此它在某些同步场景中可能更高效。

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

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

相关文章

VS2019开发CAN上位机

1、CAN分析仪&#xff0c;主要功能就是把CAN信号转换成电脑能接收的USB信号。索引号是指电脑连接了几台CAN分析仪设备&#xff0c;一般情况下都是一台&#xff0c;该值为0。不同CAN盒的二次开发文件不同 2、CAN上位机一般只能适应一个CAN盒&#xff0c;如果需要实现多个CAN盒通…

Elasticsearch - SpringBoot 查询 es 相关示例

文章目录 前言Elasticsearch - SpringBoot 查询 es1. ES 整合2. 示例-简单匹配查询3. 示例-简单范围查询4. 示例-布尔查询-分页查询-match 查询5. 示例-布尔查询-分页查询-term查询 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三…

深度学习的基础_多层感知机的手动实现

多层感知机&#xff08;Multilayer Perceptron&#xff0c;简称MLP&#xff09;是一种前馈人工神经网络。它包含至少三层节点&#xff1a;一个输入层、一个或多个隐藏层以及一个输出层。除输入节点外&#xff0c;每个节点都是一个带有非线性激活函数的神经元&#xff08;或称为…

c# net8调用vc写的dll

dll程序&#xff08;vc,x86) 头文件 extern "C" int __declspec(dllexport) WINAPI add(int a, int b);实现 int WINAPI add(int a, int b) {return a b; }c#/net8 函数声明&#xff1a; [DllImport("dll/Dll1.dll", CallingConvention CallingCo…

redis的一些重要的基础知识

文章目录 1. rehash1.1 redis的hash表的数据结构 2. AOF日志2.1 简要介绍2.2 AOF重写 3. RDB快照3.1 执行过程3.2 存在问题解决方式 1. rehash 本文只介绍数据结构和结果图&#xff0c;如果要看文字描述过程&#xff0c;可以参考链接&#xff1a;rehash的详细过程 1.1 redis的…

最近 3 个 火火火火 的开源项目!

01 为你的敏感照片添加安全水印 EasyWatermark 是由开发者 rosuH 发起的一个开源项目&#xff0c;旨在帮助用户安全、轻松地为他们的照片添加水印。 这个工具不仅能够保护你的知识产权&#xff0c;还能在一定程度上防止照片被未经授权的人使用。EasyWatermark的核心功能包括&am…

WGCNA加权基因共表达网络一步法分析学习

WGCNA&#xff08;Weighted Gene Co-expression Network Analysis&#xff0c;加权重基因共表达网络分析&#xff09; WGCNA是一种用于分析基因表达数据的系统生物学方法。主要用于识别在基因表达数据中呈现共表达模式的基因模块&#xff0c;并将这些模块与样本特征&#xff0…

LSTM-Autoencoder深度学习模型在电动机异常检测中的应用

LSTM-Autoencoder深度学习模型在电动机异常检测中的应用 LSTM-Autoencoder Deep Learning Model for Anomaly Detection in Electric Motor Citation: Lachekhab, F.; Benzaoui, M.; Tadjer, S.A.; Bensmaine, A.; Hamma, H. LSTM-Autoencoder Deep Learning Model for Anoma…

Stable Diffusion绘画 | LightFlow工作流插件:一键导入,高效生图

LightFlow 是腾讯开源的工作流插件&#xff0c;通过它可以非常轻松地导入和导出工作流文件&#xff0c;从而快速地加载整个工作流。 下载后&#xff0c;放置在&#xff1a;SD安装目录\extensions&#xff0c;重载UI 即可使用&#xff0c;插件位置在生成图片区域下方&#xff1…

2024年8月31日CSDN自动提示的用法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

视频结构化从入门到精通———检索比对类应用

检索比对类应用 1 认识“检索比对” 1.检索和比对的区别 检索和比对是信息处理和数据分析中常见的两种操作&#xff0c;虽然二者在一定程度上都有涉及到信息的提取和分析&#xff0c;但其侧重点和应用场景有所不同。检索主要关注从大规模数据集中定位相关信息&#xff0c;而比…

腾讯云TRTC无UI集成——分享屏幕主流、辅流(Vue2+JS+TRTC无UI集成)

先阐述一下问题&#xff0c;在项目中用到腾讯云的TRTC&#xff0c;A端发布A1、A2两个视频源&#xff0c;在B端订阅A1、A2使用两个view进行播放渲染 问题主流视频源和辅流视频源渲染在同一view上&#xff0c;控制台报错 // 播放远端视频 TRTCService.js; setRemoteVideo(view)…

智慧工地可视化整体解决方案(Word完整版)

第 一 章 系统总体设计 1.1 总体架构 1.1.1 系统拓扑 1.1.2 系统组成 1.2 设计概述 1.3 平台系统功能 1.3.1 总部数据看板 1.3.2 项目部数据看板 1.3.3 视频联网系统 1.3.4 实名制考勤系统 1.3.5 安全生产系统 1.3.6 塔吊安全监控子系统 1.3.7 施工升降机安全监控管系统 1.3.8 …

Codeforces Round 969 (Div. 2 ABCDE题) 视频讲解

A. Dora’s Set Problem Statement Dora has a set s s s containing integers. In the beginning, she will put all integers in [ l , r ] [l, r] [l,r] into the set s s s. That is, an integer x x x is initially contained in the set if and only if l ≤ x ≤…

Java程序天生就是多线程程序Java程序天生就是多线程程序吗?

一个Java程序从main()方法开始执行&#xff0c;然后按照既定的代码逻辑执行&#xff0c;看似没有其他线程参与&#xff0c;但实际上Java程序天生就是多线程程序&#xff0c;因为执行main()方法的是一个名称为main的线程。而一个Java程序的运行就算是没有用户自己开启的线程&…

AIGC提示词(2):塑造未来内容创作的核心力量

引言 &#x1f31f; 在这个数字化的时代&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;正变得越来越普遍。从自动写作到图像生成&#xff0c;AI正以前所未有的速度和多样性创造内容。然而&#xff0c;要实现高质量和相关性强的内容生成&#xff0c;关键在于有效地…

Python变量类型

参考&#xff1a; Python 变量类型 | 菜鸟教程 (runoob.com) 变量赋值 Python 中的变量赋值不需要类型声明&#xff08;重点注意这一点&#xff09;。 每个变量在内存中创建&#xff0c;都包括变量的标识&#xff0c;名称和数据这些信息。 每个变量在使用前都必须赋值&#xff…

golang zap日志模块封装sentry

我们自己写个log日志包&#xff0c;把zap和sentry封装到一起。 下面直接贴上主要部分代码&#xff08;两个模块初始化部分的代码请自行查阅官方文档&#xff09;&#xff1a; logger.go package logimport ("github.com/getsentry/sentry-go""go.uber.org/zap…

使用Redis如何实现集群会话同步?

使用Redis如何实现集群会话同步&#xff1f; 1、为什么选择Redis&#xff1f;2、如何实现&#xff1f;1. 环境准备2. 配置Web服务器3. 测试与验证4. 监控与优化 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在分布式Web应用中&#xff0c…

探索全能型AI与专业型AI的未来趋势

目录 前言1. 全能型AI与专业型AI的对比1.1 经济市场与用户吸引力1.2 精度与效果 2. AI模型的全面评估与比较2.1 精度2.2 速度2.3 鲁棒性 3. 专精化与可扩展性的权衡3.1 专精化的优势与挑战3.2 全能型AI的可扩展性 结语 前言 在人工智能领域&#xff0c;随着技术的迅猛发展&…