鸿蒙LiteOS信号量原理实战:信号量作用、MAX_COUNT含义、线程同步源码解析

一、前言

本文基于小凌派 RK2206鸿蒙LiteOS标准示例代码,从零讲解LiteOS内核信号量核心概念:为什么需要信号量、信号量能干什么、MAX_COUNT参数真实含义,搭配完整源码逐行解析、运行逻辑拆解,适合鸿蒙单片机、LiteOS多线程入门学习。

哔站视频《05:RK2206 OpenHarmonyOS 鸿蒙 什么是信号量 为什么需要信号量 代码演示》:https://www.bilibili.com/video/BV1to5W6pETF/?vd_source=3a9dd7a328acafb09dd1b8d05f3e2bf7

哔站视频《04:RK2206 OpenHarmonyOS 鸿蒙 任务实战》:https://www.bilibili.com/video/BV15R5E6JEHy/

哔站视频《03:RK2206 鸿蒙 LiteOS 如何通过控制编译选项编译不同案例》:https://www.bilibili.com/video/BV15e5J6QEGY/?spm_id_from=333.1387.homepage.video_card.click&vd_source=3a9dd7a328acafb09dd1b8d05f3e2bf7

哔站视频《02:RK2206 鸿蒙 LiteOS bin 文件 烧写》:https://www.bilibili.com/video/BV1pcRdBaEAt/?spm_id_from=333.1387.homepage.video_card.click&vd_source=3a9dd7a328acafb09dd1b8d05f3e2bf7

哔站视频《01:RK2206 鸿蒙 LiteOS ubuntu 开发环境 全程 安装配置》:https://www.bilibili.com/video/BV1nrRkBoEMR/?spm_id_from=333.1387.homepage.video_card.click&vd_source=3a9dd7a328acafb09dd1b8d05f3e2bf7

二、信号量核心概念通俗讲解

1. 什么是信号量

信号量可以简单理解为线程之间的通行证/令牌
线程想要执行业务逻辑,必须先申请拿到信号量;拿不到信号量就会阻塞休眠,不占用CPU资源;只有其他线程主动释放信号量后,等待的线程才能被唤醒继续执行。

2. 为什么要有信号量

裸机开发单任务顺序执行,不存在资源争抢问题;但LiteOS是多线程操作系统,多个线程会抢占CPU、同时操作共享资源,会出现执行顺序混乱、共享数据错乱、业务逻辑失控等问题。

信号量就是用来解决多线程两大核心痛点:

  1. 线程同步:控制多个线程的执行节奏和先后顺序,不让线程无序抢占运行;
  2. 资源互斥:保护串口、外设、全局变量等共享资源,保证同一时刻只有一个线程访问。

3. 信号量主要用途

  • 多线程任务同步,统一调度线程执行时机;
  • 临界资源互斥访问,防止多线程同时操作引发数据异常;
  • 线程间通信协作,一个线程触发、多个线程响应执行。

三、MAX_COUNT参数深度解析

1. 参数定义

#define MAX_COUNT                   4
// 创建计数信号量
LOS_SemCreate(MAX_COUNT, &m_sem);

2. 核心含义

MAX_COUNT计数信号量的最大上限值

  1. 信号量内部计数值永远不能超过MAX_COUNT
  2. 最多可以连续释放MAX_COUNT次信号量;
  3. 当信号量计数已经达到最大值时,再调用LOS_SemPost释放信号量会执行失败、无效累加

3. 生活化类比

把信号量比作固定车位的停车场

  • MAX_COUNT = 4:停车场总共只有4个空位;
  • LOS_SemPost:车辆驶出,空余车位+1;
  • LOS_SemPend:车辆驶入,空余车位-1;
  • 车位最多4个,无法凭空多出第5个,对应不能连续释放5次信号量。

4. 规则示例

允许连续释放:最多4次,信号量计数逐步涨到4;
禁止连续释放:计数已经为4时,再调用LOS_SemPost无效,无法变成5。

若设置MAX_COUNT = 1,就变成二值信号量,只能释放1次,常用于简单互斥锁场景。

四、完整示例源码

#include "los_sem.h"
#include "ohos_init.h"

#define MAX_COUNT 4

static unsigned int m_sem;

/***************************************************************
 * 函数名称: control_thread
 * 说    明: 控制线程函数
 * 参    数: 无
 * 返 回 值: 无
 ***************************************************************/
void control_thread()
{
    unsigned int count = 0;

    while (1)
    {
        /*释放两次信号量,sem_one_thread和sem_two_thread同步执行;
        释放一次信号量,sem_one_thread和sem_two_thread交替执行*/
        LOS_SemPost(m_sem);
        printf("control_thread Release twice Semaphore\n");
        LOS_Msleep(1000);
    }
}

/***************************************************************
 * 函数名称: sem_one_thread
 * 说    明: 信号量线程函数1
 * 参    数: 无
 * 返 回 值: 无
 ***************************************************************/
void sem_one_thread()
{
    while (1)
    {
        /*申请信号量*/
        LOS_SemPend(m_sem, LOS_WAIT_FOREVER);
        printf("sem_one_thread get Semaphore\n");
    }
}

void semaphore_example()
{
    unsigned int thread_crtl;
    unsigned int thread_id1;
    unsigned int thread_id2;
    TSK_INIT_PARAM_S task1 = {0};
    TSK_INIT_PARAM_S task2 = {0};
    TSK_INIT_PARAM_S task3 = {0};
    unsigned int ret = LOS_OK;

    ret = LOS_SemCreate(MAX_COUNT, &m_sem);
    if (ret != LOS_OK)
    {
        printf("Falied to create Semaphore\n");
        return;
    }

    // 发送任务
    task1.pfnTaskEntry = (TSK_ENTRY_FUNC)control_thread;
    task1.uwStackSize = 2048;
    task1.pcName = "control_thread";
    task1.usTaskPrio = 24;
    ret = LOS_TaskCreate(&thread_crtl, &task1);
    if (ret != LOS_OK)
    {
        printf("Falied to create control_thread ret:0x%x\n", ret);
        return;
    }

    // 接受任务
    task2.pfnTaskEntry = (TSK_ENTRY_FUNC)sem_one_thread;
    task2.uwStackSize = 2048;
    task2.pcName = "sem_one_thread";
    task2.usTaskPrio = 24;
    ret = LOS_TaskCreate(&thread_id1, &task2);
    if (ret != LOS_OK)
    {
        printf("Falied to create sem_one_thread ret:0x%x\n", ret);
        return;
    }

}

APP_FEATURE_INIT(semaphore_example);

五、源码运行逻辑解析

  1. 程序入口初始化,创建最大计数为4的信号量;
  2. 依次创建三个优先级相同的线程:控制线程、等待线程1、等待线程2;
  3. 两个等待线程一启动就调用LOS_SemPend永久阻塞等待信号量,无信号量则一直休眠;
  4. 控制线程每隔1秒执行一次逻辑:
    • 普通计数周期:释放1次信号量,两个等待线程交替执行
    • 每3个周期:连续释放2次信号量,两个等待线程同时被唤醒同步执行
  5. 全程由信号量管控线程唤醒时机,实现多线程精准同步。

六、LiteOS信号量常用API汇总

API函数 功能说明
LOS_SemCreate 创建计数信号量,设置最大计数值
LOS_SemPend 申请信号量,可设置阻塞等待时间
LOS_SemPost 释放信号量,信号量计数加1
LOS_SemDelete 删除信号量,释放内核资源

七、运行打印效果示例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

八、总结

  1. 信号量是LiteOS多线程核心同步工具,本质是线程通行证;
  2. MAX_COUNT是信号量计数上限,限制最大连续释放次数,不可超量释放;
  3. 本示例实现了一个控制线程调度两个业务线程,完美演示信号量线程同步用法;
  4. 计数信号量适合多线程同步,二值信号量适合简单资源互斥,可根据业务场景灵活选用。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐