目标:掌握 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 点击 BuildUpload,串口波特率 115200


九、下一步

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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐