鸿蒙南向开发教程 Day 8:信号量三种类型详解
对比项同步信号量计数信号量二值信号量(互斥)初始值00 ~ N1最大值1N1典型用途事件通知资源计数互斥访问Acquire 行为等对方 Release等资源可用等锁释放Release 行为通知对方增加资源计数释放锁首次 Acquire阻塞可能阻塞立即通过线程关系单向同步生产者-消费者竞争共享资源。
·
目标:掌握 OpenHarmony 轻量系统的三种信号量用法:二值信号量、计数信号量、同步信号量
前置条件:已完成 Day 7 的互斥锁教程
一、工程结构
app/
├── BUILD.gn
└── 07_semaphore/ # 模块目录
├── BUILD.gn
└── demo.c # 三种信号量测试代码
1.1 app/BUILD.gn
import("//build/lite/config/component/lite_component.gni")
lite_component("app") {
features = [
"07_semaphore:semaphore_demo",
]
}
1.2 07_semaphore/BUILD.gn
static_library("semaphore_demo") {
sources = [
"demo.c"
]
include_dirs = [
"//utils/native/lite/include",
"//kernel/liteos_m/components/cmsis/2.0",
]
}
二、信号量类型总览
| 类型 | 初始值 | 最大值 | 用途 |
|---|---|---|---|
| 二值信号量 | 1 | 1 | 互斥访问(替代 Mutex) |
| 同步信号量 | 0 | 1 | 线程间事件通知(一方等、一方发) |
| 计数信号量 | 0 ~ N | N | 资源计数管理(如停车场车位) |
三、类型一:同步信号量(初始值 0)
3.1 场景
Task1 等待 Task2 完成某个操作后才能继续,单向同步。
3.2 代码
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
osThreadId_t Task1_ID;
osThreadId_t Task2_ID;
osSemaphoreId_t Semaphore_ID;
#define TASK_STACK_SIZE 1024
#define TASK1_DELAY_TIME 1
#define TASK2_DELAY_TIME 2
// Task1:等待同步信号
void Task1(void)
{
while (1) {
// 信号量初始为 0,第一次会阻塞等待
osSemaphoreAcquire(Semaphore_ID, osWaitForever);
printf("Task 1 osSemaphore Acquire <=====\n");
sleep(TASK1_DELAY_TIME);
}
}
// Task2:发出同步信号
void Task2(void)
{
while (1) {
osSemaphoreRelease(Semaphore_ID);
printf("Task 2 osSemaphore Release =====>\n");
sleep(TASK2_DELAY_TIME);
}
}
static void kernel_sync_semaphore_example(void)
{
printf("Enter kernel_sync_semaphore_example()!\n");
// 同步信号量:初始值 0,最大值 1
// Task1 先启动,但 Acquire 会阻塞,直到 Task2 Release
Semaphore_ID = osSemaphoreNew(1, 0, NULL);
if (Semaphore_ID != NULL) {
printf("ID = %d, Create Semaphore_ID is OK!\n", Semaphore_ID);
}
osThreadAttr_t taskOptions;
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_count |
1 | 最大计数值 |
initial_count |
0 | 初始为 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_t Task1_ID;
osThreadId_t Task2_ID;
osSemaphoreId_t Semaphore_ID;
#define SEM_MAX_COUNT 10 // 停车场总车位
#define TASK_STACK_SIZE (1024)
#define TASK1_DELAY_TIME 1 // 进车频率
#define TASK2_DELAY_TIME 2 // 出车频率
// Task1:车辆进入(Release,信号量 +1)
void Task1(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)
void Task2(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);
}
}
static void kernel_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_t taskOptions;
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_count |
SEM_MAX_COUNT - 1 = 9 |
信号量最大值 |
initial_count |
0 | 初始空停车场 |
注意:代码中
max_count设为 9,实际语义应为 10,这里按原始代码保留。
信号量值含义:
| 信号量值 | 含义 |
|---|---|
| 0 | 停车场空,无车可出 |
| N | 停车场有 N 辆车 |
| max_count | 停车场满,无法进入 |
4.4 应用场景
| 场景 | 说明 |
|---|---|
| 停车场车位 | 本例 |
| 缓冲区槽位 | 环形缓冲区空位计数 |
| 连接池管理 | 可用连接数 |
| 线程池任务 | 待处理任务数 |
五、类型三:二值信号量(互斥,初始值 1)
5.1 场景
保护共享资源,功能等价于互斥锁(Day 7 内容)。
5.2 代码(回顾 Day 7 互斥场景)
osSemaphoreId_t Semaphore_ID;
uint8_t buff[20] = {0};
// 创建二值信号量:初始 1(资源可用)
Semaphore_ID = osSemaphoreNew(1, 1, NULL);
void Task1(void)
{
while (1) {
osSemaphoreAcquire(Semaphore_ID, osWaitForever); // P 操作,信号量 1→0
// 临界区:写 buff
for (int i = 0; i < sizeof(buff); i++) buff[i] = i;
osSemaphoreRelease(Semaphore_ID); // V 操作,信号量 0→1
sleep(1);
}
}
void Task2(void)
{
while (1) {
osSemaphoreAcquire(Semaphore_ID, osWaitForever); // P 操作,若 0 则阻塞
// 临界区:读 buff
for (int i = 0; i < sizeof(buff); i++) printf("%d ", buff[i]);
osSemaphoreRelease(Semaphore_ID); // V 操作
sleep(1);
}
}
5.3 关键设计
| 参数 | 值 | 含义 |
|---|---|---|
max_count |
1 | 最大值 1(二值) |
initial_count |
1 | 初始可用,第一个 Acquire 不阻塞 |
六、三种信号量对比总结
| 对比项 | 同步信号量 | 计数信号量 | 二值信号量(互斥) |
|---|---|---|---|
| 初始值 | 0 | 0 ~ N | 1 |
| 最大值 | 1 | N | 1 |
| 典型用途 | 事件通知 | 资源计数 | 互斥访问 |
| Acquire 行为 | 等对方 Release | 等资源可用 | 等锁释放 |
| Release 行为 | 通知对方 | 增加资源计数 | 释放锁 |
| 首次 Acquire | 阻塞 | 可能阻塞 | 立即通过 |
| 线程关系 | 单向同步 | 生产者-消费者 | 竞争共享资源 |
七、信号量 API 速查表
| API | 功能 |
|---|---|
osSemaphoreNew(max, initial, attr) |
创建信号量 |
osSemaphoreAcquire(id, timeout) |
获取(P 操作),计数 -1 |
osSemaphoreRelease(id) |
释放(V 操作),计数 +1 |
osSemaphoreGetCount(id) |
获取当前计数值 |
osSemaphoreDelete(id) |
删除信号量 |
八、编译与验证
VSCode 点击 Build → Upload,串口波特率 115200。
九、下一步
Day 9 预告:消息队列(Message Queue) —— 线程间安全传递数据。
更多推荐

所有评论(0)