news 2026/10/15 17:23:42

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
为什么二值信号量先释放,再获得,互斥量先获得,再释放

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

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

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(未上锁)

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

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

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

一句话记忆:

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 12:12:13

LobeChat是否支持OAuth登录?第三方鉴权集成进展

LobeChat是否支持OAuth登录&#xff1f;第三方鉴权集成进展 在构建现代AI对话系统时&#xff0c;身份认证早已不再是“有无”的问题&#xff0c;而是“如何做得更安全、更灵活、更贴近组织架构”的工程挑战。随着LobeChat这类开源聊天界面逐渐被用于团队协作和企业内部助手场景…

作者头像 李华
网站建设 2026/10/15 8:11:35

如何在笔记本上运行50量子比特模拟?:不为人知的内存压缩黑科技

第一章&#xff1a;量子计算的模拟量子计算的模拟是研究和开发量子算法的重要手段。由于当前真实量子计算机仍处于发展阶段&#xff0c;资源有限且易受噪声干扰&#xff0c;科研人员广泛依赖经典计算机来模拟量子系统的行为。通过构建量子态的数学模型&#xff0c;并在经典硬件…

作者头像 李华
网站建设 2026/10/15 13:48:23

揭秘低代码平台事件绑定难题:3步实现无缝交互逻辑

第一章&#xff1a;低代码组件的事件在低代码平台中&#xff0c;组件事件是实现交互逻辑的核心机制。通过监听用户操作或系统行为触发的事件&#xff0c;开发者可以快速构建动态响应的界面&#xff0c;而无需编写大量底层代码。事件的基本概念 事件是组件在特定条件下发出的信号…

作者头像 李华
网站建设 2026/10/14 9:24:57

别再盲目聚类了!空间转录组R语言最优算法选择指南

第一章&#xff1a;空间转录组细胞聚类的核心挑战空间转录组技术结合了基因表达谱与组织空间位置信息&#xff0c;为解析组织微环境提供了前所未有的视角。然而&#xff0c;在对空间转录组数据进行细胞聚类时&#xff0c;研究者面临多个核心挑战&#xff0c;这些挑战直接影响聚…

作者头像 李华
网站建设 2026/10/14 16:02:51

太月香学新书《中国传统香学》首发亮相

12月11日&#xff0c;第12届全球外交官中国文化之夜在京举办。该活动由上午的“全球品牌发展暨中国品牌出海论坛”及晚上的“中国文化之夜”组成。活动旨在促进各国驻华外交官、文化学者及企业精英间的文化交流与合作&#xff0c;推动文明互鉴与民心相通。 在“全球品牌发展暨…

作者头像 李华
网站建设 2026/10/15 11:50:17

2025冬暖影展奔赴广州,以光影开启时空对话

本周&#xff0c;全国艺联2025“艺术新作冬暖主题影展”携十部尚未公映的国产艺术佳作翩然落地广州。12月9日至11日&#xff0c;《爷爷奶奶那些事》、《燃比娃》、《长夜将尽》三部展映影片的主创团队惊喜现身映后交流环节&#xff0c;与羊城观众共同开启跨越时空的真挚对话&am…

作者头像 李华