一、先看三者怎么选
| 信号量 | 记录可用资源数量或发送通知 | 生产者/消费者、资源计数、按键唤醒线程 |
| 互斥量 | 保证共享资源同一时间只被一个线程访问 | 保护全局变量、设备或临界区 |
| 事件集 | 等待一个或多个事件条件 | 等任意事件、等多个条件全部满足 |
一句话记忆:
要计数,用信号量;
要加锁,用互斥量;
要组合多个条件,用事件集。
二、信号量:可计数的“通行证”
可以把信号量理解成停车场的空车位数量:
-
获取一个信号量:空位数减 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 组合等待。
网硕互联帮助中心



评论前必须登录!
注册