当前位置: 首页 > 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.jsqmd.com/news/94628/

相关文章:

  • 旧项目能否扛住PHP 8.6?3步完成兼容性评估,90%问题提前暴露
  • ShardingSphere分库分表实战
  • 基于协同过滤的微信小程序音乐推荐系统(源码+论文+部署+安装)
  • MAUI自动化测试框架选型对比(Xamarin.UITest vs .NET MAUI Test)
  • 10、全面解析Nagios配置与启动指南
  • 为什么你的协程不高效?深入理解纤维任务调度底层逻辑
  • 2025如何选择适合企业规模和需求的财税外包服务
  • QDK文档更新速递:2024年新增功能一览(限时解读)
  • Swagger UI 自定义请求头:从用户配置到请求注入的完整流程解析
  • 11、Nagios配置自动化工具指南
  • 揭秘环境监测中的时空异常:如何用R语言实现精准可视化与预警分析
  • 揭秘高精度农业监测系统:PHP如何实现毫秒级异常数据过滤
  • 【紧急必读】R Shiny多模态更新卡顿?这4个性能优化方案必须掌握
  • 农业试验数据处理指南(R语言方差分析核心技术大公开)
  • JAVA毕业设计257—基于Java+Springboot+vue3的高校招投标系统(源代码+数据库+开题)
  • swift中arview配置3d物体的碰撞事件,一定要配置name
  • n8n 2.0 中文汉化版一键部署教程 | 解除Execute Command限制
  • 太月香学新书《中国传统香学》首发亮相
  • 用梯形图+SCL玩转FactoryIO码垛控制
  • 泛型集合性能瓶颈,90%的开发者都忽略的3个关键点
  • 【Q#调试权威手册】:微软官方未公开的4个调试利器全曝光
  • IU5209:30V OVP,快充申请,1A,NTC及使能功能,4节锂电池充电管理芯片
  • 手把手教你编译PHP 8.6扩展(含10个实用调试技巧)
  • 十五五智能制造数字化工厂蓝图规划及推进路线:1 张蓝图、3 条主线、6 大系统、4 阶段路线
  • 某Boss直聘数据获取
  • 揭秘PHP 8.6性能瓶颈:如何利用新特性打造超强监控系统
  • 2025冬暖影展奔赴广州,以光影开启时空对话
  • 揭秘量子算法落地难题:3个关键突破点让你少走5年弯路
  • 为什么你的GraphQL API不够灵活?PHP字段别名设计的4个致命误区
  • 掌握这5种AOT调试黑科技,效率提升300%不是梦