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

鸿蒙南向开发教程 Day 8:信号量三种类型详解

目标:掌握 OpenHarmony 轻量系统的三种信号量用法:二值信号量、计数信号量、同步信号量
前置条件:已完成 Day 7 的互斥锁教程


一、工程结构

app/ ├── BUILD.gn └── 07_semaphore/ # 模块目录 ├── BUILD.gn └── demo.c # 三种信号量测试代码

1.1app/BUILD.gn

import("//build/lite/config/component/lite_component.gni") lite_component("app") { features = [ "07_semaphore:semaphore_demo", ] }

1.207_semaphore/BUILD.gn

static_library("semaphore_demo") { sources = [ "demo.c" ] include_dirs = [ "//utils/native/lite/include", "//kernel/liteos_m/components/cmsis/2.0", ] }

二、信号量类型总览

类型初始值最大值用途
二值信号量11互斥访问(替代 Mutex)
同步信号量01线程间事件通知(一方等、一方发)
计数信号量0 ~ NN资源计数管理(如停车场车位)

三、类型一:同步信号量(初始值 0)

3.1 场景

Task1 等待 Task2 完成某个操作后才能继续,单向同步

3.2 代码

#include<stdio.h>#include<unistd.h>#include"ohos_init.h"#include"cmsis_os2.h"osThreadId_tTask1_ID;osThreadId_tTask2_ID;osSemaphoreId_tSemaphore_ID;#defineTASK_STACK_SIZE1024#defineTASK1_DELAY_TIME1#defineTASK2_DELAY_TIME2// Task1:等待同步信号voidTask1(void){while(1){// 信号量初始为 0,第一次会阻塞等待osSemaphoreAcquire(Semaphore_ID,osWaitForever);printf("Task 1 osSemaphore Acquire <=====\n");sleep(TASK1_DELAY_TIME);}}// Task2:发出同步信号voidTask2(void){while(1){osSemaphoreRelease(Semaphore_ID);printf("Task 2 osSemaphore Release =====>\n");sleep(TASK2_DELAY_TIME);}}staticvoidkernel_sync_semaphore_example(void){printf("Enter kernel_sync_semaphore_example()!\n");// 同步信号量:初始值 0,最大值 1// Task1 先启动,但 Acquire 会阻塞,直到 Task2 ReleaseSemaphore_ID=osSemaphoreNew(1,0,NULL);if(Semaphore_ID!=NULL){printf("ID = %d, Create Semaphore_ID is OK!\n",Semaphore_ID);}osThreadAttr_ttaskOptions;taskOptions.name="Task1";taskOptions.attr_bits=0;taskOptions.cb_mem=NULL;taskOptions.cb_size=0;taskOptions.stack_mem=NULL;taskOptions.stack_size=TASK_STACK_SIZE;taskOptions.priority=osPriorityNormal;Task1_ID=osThreadNew((osThreadFunc_t)Task1,NULL,&taskOptions);if(Task1_ID!=NULL){printf("ID = %d, Create Task1_ID is OK!\n",Task1_ID);}taskOptions.name="Task2";taskOptions.priority=osPriorityNormal;Task2_ID=osThreadNew((osThreadFunc_t)Task2,NULL,&taskOptions);if(Task2_ID!=NULL){printf("ID = %d, Create Task2_ID is OK!\n",Task2_ID);}}SYS_RUN(kernel_sync_semaphore_example);

3.3 关键设计

参数含义
max_count1最大计数值
initial_count0初始为 0,Task1 首次 Acquire 阻塞

执行流程

时间轴 → Task1: [启动]──[Acquire]←──阻塞!信号量=0 │ Task2: [启动]────────────[Release]──信号量=1──→唤醒Task1 │ Task1: ←──────────────────[被唤醒]──[执行]──[Acquire]←──再次阻塞 │ Task2: ──────────────────[Release]──信号量=1──→再次唤醒 │ 循环:Task2 每 2 秒发一次信号,Task1 每收到一次执行一次

3.4 应用场景

场景说明
中断通知线程中断 ISR Release,线程 Acquire 等待
任务完成通知任务 A 完成某事后通知任务 B
生产者-消费者(单缓冲)生产者生产完通知消费者

四、类型二:计数信号量(资源管理)

4.1 场景

停车场管理:10 个车位,车辆进出计数。

4.2 代码

#include<stdio.h>#include<unistd.h>#include"ohos_init.h"#include"cmsis_os2.h"osThreadId_tTask1_ID;osThreadId_tTask2_ID;osSemaphoreId_tSemaphore_ID;#defineSEM_MAX_COUNT10// 停车场总车位#defineTASK_STACK_SIZE(1024)#defineTASK1_DELAY_TIME1// 进车频率#defineTASK2_DELAY_TIME2// 出车频率// Task1:车辆进入(Release,信号量 +1)voidTask1(void){while(1){// 检查当前车位是否已满if(osSemaphoreGetCount(Semaphore_ID)<SEM_MAX_COUNT){if(osSemaphoreRelease(Semaphore_ID)==osOK){printf("[进入%d辆车, 停车场容量: %d] 信号量+1.\n",osSemaphoreGetCount(Semaphore_ID),SEM_MAX_COUNT);}}else{printf("[进入停车场失败, 请等待...]\n");}sleep(TASK1_DELAY_TIME);}}// Task2:车辆离开(Acquire,信号量 -1)voidTask2(void){while(1){// 等待有车辆(信号量 > 0)if(osSemaphoreAcquire(Semaphore_ID,osWaitForever)==osOK){printf("[出去1辆车, 剩余停车场容量: %d] 信号量-1.\n",osSemaphoreGetCount(Semaphore_ID));}else{printf("[出停车场失败]\n");}sleep(TASK2_DELAY_TIME);}}staticvoidkernel_sync_semaphore_example(void){printf("Enter kernel_sync_semaphore_example()!\n");// 计数信号量:最大值 10,初始值 0(空停车场)Semaphore_ID=osSemaphoreNew(SEM_MAX_COUNT-1,0,NULL);if(Semaphore_ID!=NULL){printf("ID = %d, Create Semaphore_ID is OK!\n",Semaphore_ID);}osThreadAttr_ttaskOptions;taskOptions.name="Task1";taskOptions.attr_bits=0;taskOptions.cb_mem=NULL;taskOptions.cb_size=0;taskOptions.stack_mem=NULL;taskOptions.stack_size=TASK_STACK_SIZE;taskOptions.priority=osPriorityNormal;Task1_ID=osThreadNew((osThreadFunc_t)Task1,NULL,&taskOptions);if(Task1_ID!=NULL){printf("ID = %d, Create Task1_ID is OK!\n",Task1_ID);}taskOptions.name="Task2";taskOptions.priority=osPriorityNormal;Task2_ID=osThreadNew((osThreadFunc_t)Task2,NULL,&taskOptions);if(Task2_ID!=NULL){printf("ID = %d, Create Task2_ID is OK!\n",Task2_ID);}}SYS_RUN(kernel_sync_semaphore_example);

4.3 关键设计

参数含义
max_countSEM_MAX_COUNT - 1= 9信号量最大值
initial_count0初始空停车场

注意:代码中max_count设为 9,实际语义应为 10,这里按原始代码保留。

信号量值含义

信号量值含义
0停车场空,无车可出
N停车场有 N 辆车
max_count停车场满,无法进入

4.4 应用场景

场景说明
停车场车位本例
缓冲区槽位环形缓冲区空位计数
连接池管理可用连接数
线程池任务待处理任务数

五、类型三:二值信号量(互斥,初始值 1)

5.1 场景

保护共享资源,功能等价于互斥锁(Day 7 内容)。

5.2 代码(回顾 Day 7 互斥场景)

osSemaphoreId_tSemaphore_ID;uint8_tbuff[20]={0};// 创建二值信号量:初始 1(资源可用)Semaphore_ID=osSemaphoreNew(1,1,NULL);voidTask1(void){while(1){osSemaphoreAcquire(Semaphore_ID,osWaitForever);// P 操作,信号量 1→0// 临界区:写 bufffor(inti=0;i<sizeof(buff);i++)buff[i]=i;osSemaphoreRelease(Semaphore_ID);// V 操作,信号量 0→1sleep(1);}}voidTask2(void){while(1){osSemaphoreAcquire(Semaphore_ID,osWaitForever);// P 操作,若 0 则阻塞// 临界区:读 bufffor(inti=0;i<sizeof(buff);i++)printf("%d ",buff[i]);osSemaphoreRelease(Semaphore_ID);// V 操作sleep(1);}}

5.3 关键设计

参数含义
max_count1最大值 1(二值)
initial_count1初始可用,第一个 Acquire 不阻塞

六、三种信号量对比总结

对比项同步信号量计数信号量二值信号量(互斥)
初始值00 ~ N1
最大值1N1
典型用途事件通知资源计数互斥访问
Acquire 行为等对方 Release等资源可用等锁释放
Release 行为通知对方增加资源计数释放锁
首次 Acquire阻塞可能阻塞立即通过
线程关系单向同步生产者-消费者竞争共享资源

七、信号量 API 速查表

API功能
osSemaphoreNew(max, initial, attr)创建信号量
osSemaphoreAcquire(id, timeout)获取(P 操作),计数 -1
osSemaphoreRelease(id)释放(V 操作),计数 +1
osSemaphoreGetCount(id)获取当前计数值
osSemaphoreDelete(id)删除信号量

八、编译与验证

VSCode 点击BuildUpload,串口波特率115200


九、下一步

Day 9 预告:消息队列(Message Queue)—— 线程间安全传递数据。

http://www.jsqmd.com/news/949767/

相关文章:

  • 如何在Windows 11任务栏实现沉浸式歌词显示:Taskbar-Lyrics完全指南
  • 万象透骨膏小程序商城开发全攻略
  • 2026 杭州高端全屋定制优选榜单|5 大品牌从环保、设计、落地全方位测评 - 商业新知
  • 2026年5款做简历工具推荐:从空白到投递全程省心的工具盘点
  • 流年寄钻戒,踏遍杭城街巷,找寻善待往事的靠谱回收店 - 奢侈品回收评测
  • 几十页文献一分钟就能读懂,文献解读神器实测
  • LinkSwift技术解析:基于JavaScript的跨平台网盘直链获取方案
  • Tabletop Simulator终极备份指南:如何一键保护你的虚拟桌游资产
  • MATLAB版SRCNN超分重建工具包:一键运行+操作视频+21张测试图
  • 别再乱用(int)了!C/C++中浮点数转整数的‘向零取整’陷阱与正确四舍五入方法
  • 树莓派智能拍照亭:从GPIO控制到图像处理的嵌入式开发实践
  • 2026北京美国留学中介哪家好?多家机构申请优势盘点 - 品牌2026
  • AI赋能下载:让快马平台生成具备智能解析与安全扫描的下载助手
  • 基于Pixy2视觉传感器与Arduino的物体跟随机器人实战指南
  • 基于ESP32与TFT触摸屏的本地化智能灯带控制器DIY指南
  • Kafka Exporter:构建企业级Kafka监控体系的终极解决方案
  • 3步彻底掌控Windows浏览器生态:EdgeRemover脚本的完整使用手册
  • 基于NodeMCU与Blynk的WiFi伺服电机远程控制系统搭建指南
  • 从Jedis切换到Lettuce后,我的Redis客户端为啥‘感知’不到集群变化了?
  • 2026年必看!专业光催化氙灯光源方案大推荐,实用又靠谱!
  • 别再只盯着频谱了!用MATLAB提取振动信号的时域特征(附完整代码与避坑指南)
  • Python与树莓派蓝牙控制机器人:从键盘遥控到GPIO传感器集成
  • 2026年河南珍珠棉包装材料采购指南:防撞冷链保温方案全景解读 - 优质企业观察收录
  • C++与C语言的核心区别是啥
  • 如何利用开源音乐播放器 LX Music Desktop 构建你的个性化音乐生态系统
  • 怎样永久保存微信聊天记录:免费开源工具完整指南
  • STM32F407音频实时采集与播放系统:从ADC/DAC到DMA双缓冲的嵌入式实战
  • 基于同心互质圆阵与稀疏贝叶斯学习的高自由度DOA估计技术
  • Arduino智能夜灯制作:从PWM调光到RGB渐变光效实战
  • 3分钟搞定FDM 3D打印螺纹优化:Fusion 360插件终极教程