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);
编译与运行
- 将上述代码保存为
semaphore_sample.c,放入工程的applications目录。 - 在
applications/SConstruct中确认该文件已被编译(通常自动扫描)。 - 编译并烧录到STM32开发板,打开串口终端(如PuTTY,波特率115200)。
- 在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。