找回密码
 立即注册
首页 业界区 安全 RT-Thread 之信号量使用

RT-Thread 之信号量使用

欧阳梓蓓 前天 08:45
1. 信号量概述

信号量(Semaphore)是 RT-Thread 中核心的同步与互斥 IPC 机制,根据初始值可分为两类,适用场景不同:

  • 二值信号量(初始值 = 1):实现互斥访问,确保同一时间只有一个线程占用共享资源(如硬件外设、全局变量)。
  • 计数信号量(初始值 > 1):实现资源计数,允许指定数量的线程同时访问共享资源(如有限的缓冲区、总线接口)。
典型应用场景:多个传感器采集线程竞争 I2C 总线时,用初始值为 1 的二值信号量做互斥锁,避免总线数据冲突;多线程读取环形缓冲区时,用计数信号量统计可用数据量,实现生产者 - 消费者同步。
2. 信号量核心 API 函数

信号量的操作围绕 “创建 / 初始化 / 获取 / 释放 / 删除 / 脱离” 展开,需注意动态创建静态初始化的区别(动态依赖内存堆,静态基于已分配的全局 / 静态变量)。
2.1 信号量的创建与初始化

类型函数原型关键参数说明动态创建rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)- name:信号量名称(用于调试)- value:初始值(1 = 互斥,>1 = 计数)- flag:等待队列排序方式(RT_IPC_FLAG_FIFO= 先进先出;RT_IPC_FLAG_PRIO= 按线程优先级)静态初始化rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag)- sem:全局 / 静态定义的信号量对象(需提前分配内存)- 其他参数同动态创建2.2 信号量的获取

获取信号量即 “申请资源”,若信号量值 > 0 则直接占用(值 - 1),若值 = 0 则线程进入阻塞状态,直到超时或被唤醒。
函数功能描述rt_sem_take(rt_sem_t sem, rt_int32_t timeout)带超时获取:timeout= 等待时间(单位:tick,RT_WAITING_FOREVER= 永久等待,0= 无等待)rt_sem_trytake(rt_sem_t sem)无等待获取:若信号量不可用,直接返回错误(RT_EBUSY),不阻塞线程2.3 信号量的释放与销毁

释放信号量即 “归还资源”,会将信号量值 + 1,并按创建时的flag唤醒等待队列中的线程;销毁则释放信号量占用的资源。
操作函数原型适用场景释放rt_err_t rt_sem_release(rt_sem_t sem)线程使用完资源后,归还信号量动态删除rt_err_t rt_sem_delete(rt_sem_t sem)销毁动态创建的信号量(释放堆内存)静态脱离rt_err_t rt_sem_detach(rt_sem_t sem)脱离静态初始化的信号量(不释放内存)3. 信号量使用示例

3.1 源代码
  1. #include "thread_task.h"
  2. #include "main.h"
  3. #include <stdio.h>      
  4. #include "rtthread.h"
  5. #include <rthw.h>
  6. /******************************************** 线程 1 ******************************************************/
  7. #define THREAD_1_PRIORITY                  4           /* 线程优先级(值越小优先级越高) */
  8. #define THREAD_1_STACK_SIZE                512         /* 线程栈空间大小(单位:字节) */
  9. #define THREAD_1_TIMESLICE                10           /* 线程时间片个数(单位:tick) */
  10. static struct rt_thread *thread_1_handle;    /* 线程句柄 */
  11. /******************************************** 线程 2 ******************************************************/
  12. #define THREAD_2_PRIORITY                  5           /* 线程优先级(低于线程1) */
  13. #define THREAD_2_STACK_SIZE                512         /* 线程栈空间大小 */
  14. #define THREAD_2_TIMESLICE                10           /* 线程时间片个数 */
  15. static struct rt_thread *thread_2_handle;    /* 线程句柄 */
  16. struct rt_semaphore sem_test;  /* 静态信号量对象(全局定义,提前分配内存) */
  17. /**
  18. * @brief  LED闪烁函数(固定闪烁6次,即3个完整周期)
  19. * @param  time:每次翻转后的延迟时间(单位:tick)
  20. */
  21. void LED_toggle(uint16_t time)
  22. {   
  23.     for(uint8_t i = 0; i < 6; i++)  // 翻转6次 = 3个亮灭周期
  24.     {
  25.         HAL_GPIO_TogglePin(GPIOC, LED1_Pin);
  26.         rt_thread_delay(time);      // 延迟等待,模拟资源占用
  27.     }
  28. }
  29. /**
  30. * @brief  线程1入口函数(高优先级,10Hz闪烁LED)
  31. * @param  param:线程参数
  32. */
  33. void thread_1_entry(void* param)
  34. {
  35.     while(1)
  36.     {
  37.         /* 带100ms超时获取信号量:非关键任务,超时后放弃,避免阻塞 */
  38.         if (rt_sem_take(&sem_test, 100) == RT_EOK)
  39.         {
  40.             LED_toggle(100);  // 10Hz = 周期100ms → 每次延迟100ms(亮50ms+灭50ms)
  41.             rt_sem_release(&sem_test);  // 释放信号量,归还资源
  42.         }
  43.         rt_thread_delay(200);  // 线程执行间隔,降低CPU占用
  44.     }
  45. }
  46. /**
  47. * @brief  线程2入口函数(低优先级,1Hz闪烁LED)
  48. * @param  param:线程参数
  49. */
  50. void thread_2_entry(void* param)
  51. {   
  52.     while(1)
  53.     {
  54.         /* 永久等待获取信号量:关键任务,必须拿到资源才执行 */
  55.         if (rt_sem_take(&sem_test, RT_WAITING_FOREVER) == RT_EOK)
  56.         {
  57.             LED_toggle(1000);  // 1Hz = 周期1000ms → 每次延迟1000ms(亮500ms+灭500ms)
  58.             rt_sem_release(&sem_test);  // 释放信号量,归还资源
  59.         }
  60.         rt_thread_delay(300);  // 线程执行间隔
  61.     }
  62. }
  63. /**
  64. * @brief  初始化信号量并创建启动线程
  65. */
  66. void ThreadStart(void)
  67. {
  68.     rt_base_t level = rt_hw_interrupt_disable();
  69.     /* 静态初始化信号量) */
  70.     rt_sem_init(
  71.         &sem_test,               /* 信号量对象 */
  72.         "sem_test",              /* 信号量名称(用于finsh调试) */
  73.         1,                       /* 初始值=1 → 二值信号量(互斥模式) */
  74.         RT_IPC_FLAG_FIFO         /* 等待队列按FIFO排序 */
  75.     );
  76.     /* 动态创建并启动线程1 */
  77.     thread_1_handle = rt_thread_create(
  78.         "thread_1",                                /* 线程名称 */
  79.         thread_1_entry,                        /* 线程入口函数 */
  80.         RT_NULL,                                /* 线程参数 */
  81.         THREAD_1_STACK_SIZE,        /* 线程栈大小 */
  82.         THREAD_1_PRIORITY,                /* 线程优先级 */
  83.         THREAD_1_TIMESLICE          /* 线程时间片 */
  84.     );
  85.     if (thread_1_handle != RT_NULL)
  86.         rt_thread_startup(thread_1_handle);  
  87.     /* 动态创建并启动线程2 */
  88.     thread_2_handle = rt_thread_create(
  89.         "thread_2",                                /* 线程名称 */
  90.         thread_2_entry,                        /* 线程入口函数 */
  91.         RT_NULL,                                /* 线程参数 */
  92.         THREAD_2_STACK_SIZE,        /* 线程栈大小 */
  93.         THREAD_2_PRIORITY,                /* 线程优先级 */
  94.         THREAD_2_TIMESLICE          /* 线程时间片 */
  95.     );
  96.     if (thread_2_handle != RT_NULL)
  97.         rt_thread_startup(thread_2_handle);  
  98.     rt_hw_interrupt_enable(level);  // 恢复中断
  99. }
复制代码
3.2 代码执行流程

3.2.1 初始阶段(资源初始化)


  • 调用ThreadStart时,先关闭中断,确保信号量初始化不被打断;
  • 静态初始化信号量sem_test,初始值 = 1(二值互斥模式),等待队列按 FIFO 排序;
  • 动态创建线程 1(优先级 4)和线程 2(优先级 5),并启动线程;
  • 恢复中断,线程进入就绪状态,RT-Thread 调度器开始调度。
3.2.2 竞争阶段(高优先级线程优先)


  • 线程 1 优先级(4)高于线程 2(5),调度器优先切换线程 1 执行;
  • 线程 1 调用rt_sem_take,信号量值 1→0,获取成功,开始执行LED_toggle(100)(10Hz 闪烁 6 次,耗时 600ms);
  • 线程 2 同时调用rt_sem_take,信号量值为 0,进入永久阻塞状态,等待信号量释放。
3.2.3 优先级调度阶段(高优先级线程抢占)


  • 线程 1 闪烁完成后,调用rt_sem_release释放信号量,值 0→1,同时唤醒阻塞的线程 2;
  • 此时线程 1 因优先级更高,调度器优先将线程 1 切换为就绪状态,线程 1 再次调用rt_sem_take,优先获取信号量,继续执行;
  • 线程 2 被唤醒后,发现信号量已被线程 1 占用,再次进入阻塞状态,等待下一次信号量释放。
3.2.4 交替执行特征(LED 表现)


  • 线程 1(高优先级)获取信号量的概率更高,LED 频繁以 10Hz 快速闪烁;
  • 仅当线程 1 释放信号量后,进入rt_thread_delay(200)延迟时(线程 1 阻塞),线程 2 才能获取信号量,执行 1Hz 慢速闪烁;
  • 最终 LED 表现:以 10Hz 快速闪烁为主,偶尔插入 1Hz 慢速闪烁,无闪烁混乱(信号量互斥生效)。
3.2.5 同步效果


  • 信号量确保 LED 硬件资源同一时间仅被一个线程控制,避免并发操作导致的闪烁频率混乱;
  • 优先级机制保证高优先级线程(线程 1)的实时性,满足快速响应需求;
  • 线程 1 的 100ms 超时获取,避免因资源长期被占用导致自身阻塞;线程 2 的永久等待,确保关键闪烁任务不丢失。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册