当前位置: 首页 > 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.gsyq.cn/news/1461301.html

相关文章:

  • 2026 杭州高端全屋定制优选榜单|5 大品牌从环保、设计、落地全方位测评 - 商业新知
  • 流年寄钻戒,踏遍杭城街巷,找寻善待往事的靠谱回收店 - 奢侈品回收评测
  • 几十页文献一分钟就能读懂,文献解读神器实测
  • 2026北京美国留学中介哪家好?多家机构申请优势盘点 - 品牌2026
  • 3步彻底掌控Windows浏览器生态:EdgeRemover脚本的完整使用手册
  • 基于ESP32与TFT触摸屏的本地化智能灯带控制器DIY指南
  • 基于NodeMCU与Blynk的WiFi伺服电机远程控制系统搭建指南
  • 怎样永久保存微信聊天记录:免费开源工具完整指南
  • C++与C语言的核心区别是啥
  • Python与树莓派蓝牙控制机器人:从键盘遥控到GPIO传感器集成
  • 3步快速安装ModTheSpire:杀戮尖塔模组加载器终极使用指南
  • 基于Arduino与3D打印的SMARS机器人制作全攻略:从蓝牙遥控到智能避障
  • GUI是什么?超级详细
  • DeepSeek-V4升级解析:长上下文推理与指令遵循能力跃迁
  • 从希拉穆仁到呼伦贝尔:2026内蒙古草原旅游的甄选逻辑与实践指南 - 深度智识库
  • 【监管合规优先的AI担保方案】:覆盖银保监2024新规的6层可信验证机制详解
  • RC电路阻抗与相位分析:从公式推导到KiCad仿真实践
  • CyberpunkSaveEditor:专业级存档编辑解决方案与安全数据管理指南
  • 别再混淆了!深入对比Linux下SO_REUSEADDR和SO_REUSEPORT的实战区别
  • 【深度解析】Hermes Agent Web UI:自托管长期记忆智能体的架构、场景与 Python 实战
  • RPA自动化进阶:独立开发店群系统实战,我用底层隔离与并发调度砍掉80%人力成本
  • 你的VoLTE电话为什么突然断了?从480、487到504,揭秘那些‘隐藏’的网络切换与超时问题
  • 2026年PDF转JPG详细教程:免费在线、Windows自带、Mac预览、零软件全方案 - 软件小管家
  • 基于Arduino与NRF24L01的无线遥控炮台:从原理到实现的完整指南
  • AI工具与智能抵押整合:92%的金融机构尚未掌握的7个合规性避坑指南(附央行最新监管沙盒白皮书解读)
  • 大模型服务故障的七层架构解析与稳定性应对
  • 面试潜规则⑨:“回去等通知吧”——这句话背后的5种潜台词
  • YoloMouse终极指南:如何免费自定义游戏光标提升操作精度
  • EduCoder实训攻略:从‘刷答案’到‘真学习’,我的高效通关与知识管理实践
  • 基于树莓派Zero W与PIR传感器的户外智能监控系统DIY指南