云计算百科
云计算领域专业知识百科平台

RTT学习-信号量、互斥量、事件集

一、先看三者怎么选

内核对象核心作用典型场景
信号量 记录可用资源数量或发送通知 生产者/消费者、资源计数、按键唤醒线程
互斥量 保证共享资源同一时间只被一个线程访问 保护全局变量、设备或临界区
事件集 等待一个或多个事件条件 等任意事件、等多个条件全部满足

一句话记忆:

要计数,用信号量;
要加锁,用互斥量;
要组合多个条件,用事件集。

二、信号量:可计数的“通行证”

可以把信号量理解成停车场的空车位数量:

  • 获取一个信号量:空位数减 1;

  • 释放一个信号量:空位数加 1;

  • 数值为 0 时:申请线程等待。

核心接口:

rt_sem_take(sem, timeout);  /* 获取,成功返回 RT_EOK */
rt_sem_release(sem);        /* 释放 */

timeout 的单位是 OS Tick,也可以使用 RT_WAITING_FOREVER 一直等待。

信号量常见的两种用法:

  • 初始值为 0:用于线程间通知;

  • 初始值大于 0:表示可同时使用的资源数量。

等待队列支持:

  • RT_IPC_FLAG_FIFO:先等待的线程先获得信号量;

  • RT_IPC_FLAG_PRIO:优先级更高的等待线程先获得信号量。

信号量没有所有权,释放者不一定是获取者。因此它适合通知和计数,但保护共享资源时通常更推荐互斥量。

三、互斥量:带所有权的“门锁”

互斥量用于保护临界区:线程进入前加锁,离开后解锁,其他线程必须等待。

if (rt_mutex_take(mutex, RT_WAITING_FOREVER) == RT_EOK)
{
   /* 安全访问共享资源 */

   rt_mutex_release(mutex);
}

互斥量有三个重要特点:

  • 具有所有权:只能由持有它的线程释放;

  • 支持递归获取:同一线程可以多次获取,但必须释放相同次数;

  • 支持优先级继承:用于缓解优先级翻转。

  • 什么是优先级翻转

    假设低优先级线程 L 持有互斥量,高优先级线程 H 等待该互斥量。此时如果中优先级线程 M 不断抢占 L,H 也会被间接拖延。

    RT-Thread 会临时提高 L 的优先级,让它尽快完成临界区并释放互斥量;释放后,L 恢复原优先级。这就是优先级继承。

    临界区应尽量短,不要在持有互斥量时进行长时间延时或阻塞操作。

    课件说明,互斥量创建接口中的 flag 参数已不再区分 FIFO 和 PRIO,内核按优先级方式处理。

    四、事件集:用位表示多个条件

    事件集使用一个 32 位无符号整数保存事件,每一位代表一个事件:

    #define EVENT_KEY1 (1U << 0)
    #define EVENT_KEY2 (1U << 1)

    发送事件:

    rt_event_send(event, EVENT_KEY1);

    接收事件时可以选择不同的组合条件。

    等任意一个事件:OR

    rt_event_recv(event,
                 EVENT_KEY1 | EVENT_KEY2,
                 RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                 RT_WAITING_FOREVER,
                 &received);

    KEY1 或 KEY2 任意一个发生,线程就会被唤醒。

    等全部事件:AND

    rt_event_recv(event,
                 EVENT_KEY1 | EVENT_KEY2,
                 RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
                 RT_WAITING_FOREVER,
                 &received);

    只有 KEY1 和 KEY2 都发生,线程才会被唤醒。

    三个选项的作用:

    选项含义
    RT_EVENT_FLAG_OR 任意一个目标事件满足即可
    RT_EVENT_FLAG_AND 所有目标事件都满足才可以
    RT_EVENT_FLAG_CLEAR 接收成功后清除对应事件位

    事件集只传递“是否发生”,不传递具体数据,也不记录同一事件发生了多少次。同一事件在被读取前连续发送多次,效果仍等同于一次。

    五、创建与删除接口

    三种内核对象都支持静态和动态创建,接口必须正确配对:

    对象静态创建/删除动态创建/删除
    信号量 rt_sem_init() / rt_sem_detach() rt_sem_create() / rt_sem_delete()
    互斥量 rt_mutex_init() / rt_mutex_detach() rt_mutex_create() / rt_mutex_delete()
    事件集 rt_event_init() / rt_event_detach() rt_event_create() / rt_event_delete()

    静态方式由用户提供控制块;动态方式由系统从堆中分配控制块。

    六、三者关键区别

    对比项信号量互斥量事件集
    是否计数 否,只保存事件位
    是否有所有权
    是否支持优先级继承
    是否能组合多个条件 是,支持 AND/OR
    是否传输数据

    如果需要在线程之间传输具体数据,应使用消息队列、邮箱等通信机制,而不是事件集。

    七、常见易错点

  • 用信号量长期保护共享资源,忽略优先级翻转风险;

  • 由非持有线程释放互斥量;

  • 持有互斥量时执行长时间延时;

  • 把事件集当计数器,认为重复发送会累计次数;

  • 忘记 RT_EVENT_FLAG_CLEAR,导致旧事件位继续满足下一次等待;

  • 混用静态对象的 detach 和动态对象的 delete。

  • 八、总结

    初学时只需抓住三条主线:

  • 信号量是计数值,适合资源计数和线程通知;

  • 互斥量有所有权和优先级继承,适合保护共享资源;

  • 事件集用位记录多个条件,可通过 AND 或 OR 组合等待。

  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » RTT学习-信号量、互斥量、事件集
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!