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

为什么二值信号量先释放,再获得,互斥量先获得,再释放

我是嵌入式学习菌,一名热爱学习的嵌入式工程师

关注我,一起变得更加优秀!

CSDN、B 站视频号同名同步分享嵌入式学习点滴~ 无捷径唯有坚持,愿与你并肩稳步前行!
15篇原创内容
公众号

要理解二值信号量“先释放后获取”和互斥量“先获取后释放”的核心差异,本质是两者的设计目的和应用场景完全不同:

二值信号量:核心是同步(事件通知)——一个任务/中断通知另一个任务“某个事件已发生”,因此“事件产生方(释放信号量)”先触发,“事件消费方(获取信号量)”后响应;

互斥量:核心是互斥(资源保护)——保护多个任务共享的资源(如串口、全局变量),必须先“抢占锁(获取互斥量)”才能访问资源,用完后“释放锁(释放互斥量)”,否则会导致资源访问混乱。

以下结合ESP32+ESP-IDF环境给出具体示例,并解释顺序的必要性。

一、二值信号量:先释放、后获取(同步场景)

核心逻辑

二值信号量是“事件通知工具”:只有当「事件产生方」完成事件(如传感器采集完成、数据接收完成),才会释放信号量;「事件消费方」一直等待信号量,直到收到通知才执行后续逻辑。如果反过来“先获取后释放”,消费方会因信号量初始值为0而永久阻塞,程序完全无法运行。

ESP32IDF示例代码(同步场景:传感器采集→数据处理)

#include<stdio.h>#include"freertos/FreeRTOS.h"#include"freertos/task.h"#include"freertos/semphr.h"#include"esp_log.h"#defineTAG"BIN_SEM"staticSemaphoreHandle_txBinarySem=NULL;//任务1:事件产生方(传感器采集数据,完成后释放信号量)staticvoidtask_sensor_collect(void*arg){(void)arg;TickType_txLastWakeTime=xTaskGetTickCount();constTickType_txPeriod=pdMS_TO_TICKS(3000);//3秒采集一次for(;;){vTaskDelayUntil(&xLastWakeTime,xPeriod);//模拟:传感器采集数据完成(事件发生)ESP_LOGI(TAG,"任务1:传感器数据采集完成→释放信号量(发通知)");xSemaphoreGive(xBinarySem);//先释放:事件发生,发通知}vTaskDelete(NULL);}//任务2:事件消费方(等待信号量,收到通知后处理数据)staticvoidtask_data_process(void*arg){(void)arg;for(;;){//后获取:等待“采集完成”的通知(信号量)xSemaphoreTake(xBinarySem,portMAX_DELAY);ESP_LOGI(TAG,"任务2:收到信号量→开始处理传感器数据\n");}vTaskDelete(NULL);}voidapp_main(void){//创建二值信号量(初始值为0:无事件通知)xBinarySem=xSemaphoreCreateBinary();if(xBinarySem==NULL){ESP_LOGE(TAG,"信号量创建失败");return;}//创建任务(任务1优先级2>任务2优先级1,确保释放后立即调度)xTaskCreate(task_sensor_collect,"sensor",2048,NULL,2,NULL);xTaskCreate(task_data_process,"process",2048,NULL,1,NULL);}

运行效果(关键看顺序)

I(3015)BIN_SEM:任务1:传感器数据采集完成→释放信号量(发通知)

I(3015)BIN_SEM:任务2:收到信号量→开始处理传感器数据

I(6015)BIN_SEM:任务1:传感器数据采集完成→释放信号量(发通知)

I(6015)BIN_SEM:任务2:收到信号量→开始处理传感器数据

为什么必须“先释放后获取”?

二值信号量初始值为0,若任务2先调用xSemaphoreTake(),会直接阻塞;只有任务1先调用xSemaphoreGive()将信号量置1,任务2才能获取到信号量并执行——这正是“事件通知”的核心:先有事件,后有响应。

二、互斥量:先获取、后释放(互斥场景)

核心逻辑

互斥量是“资源锁”:多个任务访问共享资源(如串口、全局变量、硬件外设)时,必须先“上锁(获取互斥量)”,确保只有自己能访问资源;用完后“解锁(释放互斥量)”,其他任务才能抢占。如果反过来“先释放后获取”,会因“锁未被持有”导致释放失败(返回pdFALSE),且共享资源会被多个任务同时访问,造成数据混乱/打印错乱。

ESP32IDF示例代码(互斥场景:两个任务共享串口打印)

#include<stdio.h>#include"freertos/FreeRTOS.h"#include"freertos/task.h"#include"freertos/semphr.h"#include"esp_log.h"#defineTAG"MUTEX_DEMO"staticSemaphoreHandle_txMutex=NULL;//互斥量句柄staticintg_shared_count=0;//共享全局变量//任务1:访问共享资源(先获取互斥量,后释放)staticvoidtask1_access_resource(void*arg){(void)arg;for(;;){//第一步:先获取互斥量(上锁)→独占共享资源if(xSemaphoreTake(xMutex,portMAX_DELAY)==pdTRUE){//临界区:访问共享资源(全局变量+串口打印)g_shared_count++;ESP_LOGI(TAG,"任务1:持有互斥量→共享变量值:%d",g_shared_count);vTaskDelay(pdMS_TO_TICKS(500));//模拟资源占用时间//第二步:释放互斥量(解锁)→允许其他任务访问xSemaphoreGive(xMutex);ESP_LOGI(TAG,"任务1:释放互斥量\n");}vTaskDelay(pdMS_TO_TICKS(1000));//任务1执行间隔}vTaskDelete(NULL);}//任务2:访问同一个共享资源(同样先获取、后释放)staticvoidtask2_access_resource(void*arg){(void)arg;for(;;){//第一步:先获取互斥量(上锁)if(xSemaphoreTake(xMutex,portMAX_DELAY)==pdTRUE){//临界区:访问共享资源g_shared_count++;ESP_LOGI(TAG,"任务2:持有互斥量→共享变量值:%d",g_shared_count);vTaskDelay(pdMS_TO_TICKS(500));//模拟资源占用时间//第二步:释放互斥量(解锁)xSemaphoreGive(xMutex);ESP_LOGI(TAG,"任务2:释放互斥量\n");}vTaskDelay(pdMS_TO_TICKS(1000));//任务2执行间隔}vTaskDelete(NULL);}voidapp_main(void){//创建互斥量(ESP-IDF中xSemaphoreCreateMutex()初始值为1:未上锁)xMutex=xSemaphoreCreateMutex();if(xMutex==NULL){ESP_LOGE(TAG,"互斥量创建失败");return;}//创建两个优先级相同的任务(模拟资源竞争)xTaskCreate(task1_access_resource,"task1",2048,NULL,1,NULL);xTaskCreate(task2_access_resource,"task2",2048,NULL,1,NULL);}

运行效果(关键:无资源竞争,打印有序)

I(1015)MUTEX_DEMO:任务1:持有互斥量→共享变量值:1

I(1515)MUTEX_DEMO:任务1:释放互斥量

I(2015)MUTEX_DEMO:任务2:持有互斥量→共享变量值:2

I(2515)MUTEX_DEMO:任务2:释放互斥量

I(3015)MUTEX_DEMO:任务1:持有互斥量→共享变量值:3

I(3515)MUTEX_DEMO:任务1:释放互斥量

若反过来“先释放后获取”会怎样?

如果把任务1的逻辑改成:

//错误示例:先释放(未持有锁),后获取xSemaphoreGive(xMutex);//释放未持有的互斥量→返回pdFALSE,无效果xSemaphoreTake(xMutex,portMAX_DELAY);

运行结果:

xSemaphoreGive()返回pdFALSE(释放失败,因为任务1未持有互斥量);

两个任务会同时进入临界区,共享变量打印错乱(比如同时打印g_shared_count,出现值重复/跳变):

plaintext

I(1015)MUTEX_DEMO:任务1:持有互斥量→共享变量值:1

I(1015)MUTEX_DEMO:任务2:持有互斥量→共享变量值:1//数据错乱

为什么必须“先获取后释放”?

互斥量初始值为1(“未上锁”),只有先调用xSemaphoreTake()将其置0(“上锁”),才能独占共享资源;用完后调用xSemaphoreGive()置1(“解锁”),其他任务才能继续抢占——这是“资源保护”的核心:先占锁,再用资源,用完解锁。

三、核心区别总结

特性二值信号量互斥量

核心目的同步(事件通知)互斥(资源保护)

初始值0(无事件)1(未上锁)

操作顺序先释放(发通知)→后获取(收通知)先获取(上锁)→后释放(解锁)

失败后果消费方阻塞(无事件响应)资源竞争(数据/打印错乱)

典型场景传感器采集→数据处理、中断通知任务多任务访问串口/全局变量/外设

一句话记忆:

二值信号量是“发消息”:先有人发(释放),才能有人收(获取);

互斥量是“借钥匙”:先借到钥匙(获取),才能开门用资源,用完还钥匙(释放)。

http://www.cnnetsun.cn/news/68938.html

相关文章:

  • 性能监控在DevOps中的角色
  • RN Hooks 设计规范与反模式清单
  • 《Advanced Science》最新研究:多自由度折纸模块构建可编程机械超材料网络
  • 用梯形图+SCL玩转FactoryIO码垛控制
  • 7、Nagios 安装与功能拓展全解析
  • 读懂 NVIDIA Jetson OP-TEE 官方源码:从目录结构到 JetPack / Yocto 构建与运行的完整指南
  • LobeChat能否实现邀请码注册机制?控制用户增长节奏
  • Angular AOT编译失败?这份官方文档解读帮你10分钟定位问题
  • PHP环境下医疗数据备份怎么做?5种高可用方案对比分析
  • 【Python库选型避坑手册】:5年踩坑经验总结出的7条黄金法则
  • PHP 8.6 JIT编译器重大升级(指令优化黑科技曝光)
  • Keil串口通信全教程:UART初始化、数据收发(中断/查询模式)+串口调试助手验证
  • 揭秘WebSocket频繁断线之谜:3种常见错误码分析与修复方案
  • LPDDR6 JEDEC 原文解读学习—2.4 Data Packet Format(3)(~持续更新)
  • 【PHP性能优化关键一步】:深入PHP 8.6内存泄漏监控与自动预警方案
  • PHP 8.6兼容性测试实战(资深架构师亲授迁移经验)
  • 从入门到精通:用R Shiny打造可交互的多维度数据仪表盘
  • 光伏阵列遇到局部阴影就像吃火锅被隔壁桌抢肉——憋屈得很。今天咱们用Python整活,搞个3×3电池板的阴影仿真模型,重点观察串联结构在阴影下的输出曲线怎么抽风
  • 你还不知道HTTP/3的这5个性能秘密?:资深架构师20年经验倾囊相授
  • 如何将EF Core响应时间降低80%?一线大厂都在用的4种策略
  • BGP综合实验
  • 大数据采集与处理技术实训室
  • 基于MATLAB 火灾检测系统,可以实现图片的火苗检测。 推荐matlab2019A及以后。
  • Harmony学习之本地数据存储
  • 【PHP扩展性能优化秘籍】:基于Rust的函数调试与内存泄漏排查指南
  • Flutter 状态管理终极指南(一):从 setState 到 Riverpod 2.0
  • Symfony 8路由缓存机制揭秘:如何让应用加载快如闪电
  • 基于STM32智能营养称系统的设计与实现_352
  • PHP 8.6升级必看:5个关键兼容性检测步骤,避免生产环境崩溃
  • 医疗行业PHP数据备份最佳实践(20年专家亲授方案)