4. 线程同步之信号量
任务目标
在 Zephyr RTOS 中,使用信号量(Semaphore)实现两个任务之间的同步,并演示信号量作为资源计数器的基本用法。本案例将创建两个线程:一个生产者线程周期性释放信号量,一个消费者线程获取信号量后执行处理,以此展示线程间的同步与资源计数机制。
环境依赖
本案例基于 Zephyr RTOS 3.x,使用 native_sim 或任意支持的内核模拟目标。需要确保 CONFIG_SEMAPHORE 已启用(Zephyr 默认启用)。无需额外硬件依赖。
完整实现
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
/* 定义信号量:最大计数为 3,初始计数为 0 */
K_SEM_DEFINE(my_sem, 0, 3);
/* 生产者线程:每 500ms 释放一个信号量 */
void producer_thread(void *arg1, void *arg2, void *arg3)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);
while (1) {
k_sem_give(&my_sem);
printk("Producer: semaphore given (count=%u)\n",
k_sem_count_get(&my_sem));
k_sleep(K_MSEC(500));
}
}
/* 消费者线程:获取信号量,若成功则处理 */
void consumer_thread(void *arg1, void *arg2, void *arg3)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);
while (1) {
k_sem_take(&my_sem, K_FOREVER);
printk("Consumer: semaphore taken (count=%u)\n",
k_sem_count_get(&my_sem));
/* 模拟处理耗时 */
k_sleep(K_MSEC(200));
}
}
/* 定义并初始化两个线程 */
K_THREAD_DEFINE(producer_tid, 1024,
producer_thread, NULL, NULL, NULL,
5, 0, 0);
K_THREAD_DEFINE(consumer_tid, 1024,
consumer_thread, NULL, NULL, NULL,
4, 0, 0);
构建与运行
- 将上述代码保存为
main.c,放入一个 Zephyr 应用工程的src目录。 - 在应用目录下创建
prj.conf(本案例无需额外配置项,文件可为空或仅包含注释)。 - 使用以下命令构建并运行(以
native_sim为例):
west build -b native_sim .
west build -t run
验证方法
程序启动后,观察串口输出。预期输出模式如下(具体计数可能因调度时序略有差异):
Producer: semaphore given (count=1)
Consumer: semaphore taken (count=0)
Producer: semaphore given (count=1)
Consumer: semaphore taken (count=0)
...
通过条件:
- 生产者与消费者交替打印,且消费者每次获取信号量后计数减 1,生产者释放后计数加 1。
- 信号量计数始终不超过 3(最大限制)。若生产者连续释放超过 3 次而消费者未及时获取,计数将饱和在 3,后续
k_sem_give不会增加计数——可观察计数稳定在 3 来验证资源上限。 - 消费者使用
K_FOREVER阻塞等待,不会出现空转忙等。
若需验证资源计数上限,可将消费者线程优先级调低或删除消费者线程,观察生产者连续释放时计数封顶在 3。
关键说明
K_SEM_DEFINE的第三个参数为信号量最大计数,用于限制资源数量;第二个参数为初始可用计数。- 生产者优先级(5)高于消费者(4),数值越小优先级越高,因此生产者会先运行并释放信号量。
- 本案例仅演示信号量的同步与计数功能,不涉及互斥访问共享资源;如需互斥保护,应使用互斥量(Mutex)。