4. 信号量同步

任务目标

在RT-Thread上创建两个线程:生产者线程周期性发送事件(模拟数据就绪),消费者线程等待信号量后处理事件。通过信号量实现两者间的同步,确保消费者仅在事件产生后才执行处理逻辑。

实现原理

信号量维护一个计数器,生产者通过rt_sem_release释放信号量(计数+1),消费者通过rt_sem_take获取信号量(计数-1)。当计数为0时,消费者线程挂起等待,直到生产者释放信号量将其唤醒。本例使用二值信号量(初始值0)实现事件通知。

完整代码

#include <rtthread.h>

#define THREAD_PRIORITY      25
#define THREAD_STACK_SIZE    512
#define THREAD_TIMESLICE     5

/* 信号量控制块 */
static rt_sem_t event_sem = RT_NULL;

/* 生产者线程:产生事件 */
static void producer_entry(void *parameter)
{
    rt_uint32_t count = 0;
    while (1)
    {
        count++;
        rt_kprintf("producer: event %d generated\n", count);
        rt_sem_release(event_sem);   /* 释放信号量,通知消费者 */
        rt_thread_mdelay(1000);      /* 每1秒产生一次事件 */
    }
}

/* 消费者线程:等待并处理事件 */
static void consumer_entry(void *parameter)
{
    rt_uint32_t count = 0;
    while (1)
    {
        rt_sem_take(event_sem, RT_WAITING_FOREVER);  /* 等待信号量 */
        count++;
        rt_kprintf("consumer: event %d processed\n", count);
    }
}

/* 信号量示例初始化 */
int semaphore_sample(void)
{
    /* 创建二值信号量,初始值为0 */
    event_sem = rt_sem_create("event_sem", 0, RT_IPC_FLAG_PRIO);
    if (event_sem == RT_NULL)
    {
        rt_kprintf("create semaphore failed\n");
        return -1;
    }

    /* 创建生产者线程 */
    rt_thread_t producer = rt_thread_create("producer",
                                            producer_entry,
                                            RT_NULL,
                                            THREAD_STACK_SIZE,
                                            THREAD_PRIORITY,
                                            THREAD_TIMESLICE);
    if (producer != RT_NULL)
        rt_thread_startup(producer);

    /* 创建消费者线程 */
    rt_thread_t consumer = rt_thread_create("consumer",
                                            consumer_entry,
                                            RT_NULL,
                                            THREAD_STACK_SIZE,
                                            THREAD_PRIORITY - 1,
                                            THREAD_TIMESLICE);
    if (consumer != RT_NULL)
        rt_thread_startup(consumer);

    return 0;
}

/* 导出命令到MSH */
MSH_CMD_EXPORT(semaphore_sample, semaphore sync sample);

编译与运行

  1. 将上述代码保存为semaphore_sample.c,放入工程的applications目录。
  2. 在applications/SConstruct中确认该文件已被编译(通常自动扫描)。
  3. 编译并烧录到STM32开发板,打开串口终端(如PuTTY,波特率115200)。
  4. 在MSH控制台输入命令:
msh> semaphore_sample

验证方法

观察串口输出,应看到生产者与消费者交替打印,且消费者处理次数始终跟随生产者产生次数,例如:

producer: event 1 generated
consumer: event 1 processed
producer: event 2 generated
consumer: event 2 processed
...

若消费者打印缺失或滞后,说明信号量同步逻辑有误。可通过list_sem命令查看信号量状态:

msh> list_sem
sem    value  holder  suspend
event_sem  0      0      0

正常情况下value在0和1之间跳变,suspend为0(无线程挂起)。

关键说明

  • 消费者线程优先级设为THREAD_PRIORITY - 1(数值更小,优先级更高),确保事件产生后消费者能立即抢占处理。
  • 使用RT_WAITING_FOREVER使消费者在无事件时永久挂起,不占用CPU。
  • 若需超时保护,可将RT_WAITING_FOREVER替换为rt_tick_from_millisecond(500),并检查返回值是否为RT_EOK。