事件集介绍来源于RT-Thread 官方文档中心,下面蓝色字体是链接
事件集的使用场合
事件集可使用于多种场合,它能够在一定程度上替代信号量,用于线程间同步。一个线程或中断服务例程发送一个事件给事件集对象,而后等待的线程被唤醒并对相应的事件进行处理。但是它与信号量不同的是,事件的发送操作在事件未清除前,是不可累计的,而信号量的释放动作是累计的。事件的另一个特性是,接收线程可等待多种事件,即多个事件对应一个线程或多个线程。同时按照线程等待的参数,可选择是 “逻辑或” 触发还是 “逻辑与” 触发。这个特性也是信号量等所不具备的,信号量只能识别单一的释放动作,而不能同时等待多种类型的释放。如下图所示为多事件接收示意图:
一个事件集中包含 32 个事件,特定线程只等待、接收它关注的事件。可以是一个线程等待多个事件的到来(线程 1、2 均等待多个事件,事件间可以使用 “与” 或者 “或” 逻辑触发线程),也可以是多个线程等待一个事件的到来(事件 25)。当有它们关注的事件发生时,线程将被唤醒并进行后续的处理动作。
事件集也是线程间同步的机制之一,一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步。
即一个线程与多个事件的关系可设置为:其中任意一个事件唤醒线程,或几个事件都到达后才唤醒线程进行后续的处理;同样,事件也可以是多个线程同步多个事件。这种多个事件的集合可以用一个 32 位无符号整型变量来表示,变量的每一位代表一个事件,线程通过 “逻辑与” 或“逻辑或”将一个或多个事件关联起来,形成事件组合。事件的 “逻辑或” 也称为是独立型同步,指的是线程与任何事件之一发生同步;事件 “逻辑与” 也称为是关联型同步,指的是线程与若干事件都发生同步。
RT-Thread 定义的事件集有以下特点:
1)事件只与线程相关,事件间相互独立:每个线程可拥有 32 个事件标志,采用一个 32 bit 无符号整型数进行记录,每一个 bit 代表一个事件;
2)事件仅用于同步,不提供数据传输功能;
3)事件无排队性,即多次向线程发送同一事件 (如果线程还未来得及读走),其效果等同于只发送一次。
事件集相关函数接口介绍
下面一段代码创建三个线程,th1,th2,th3,3个EVENT_FLAG,
线程1 :接收到事件1EVENT_FLAG1,执行线程1,并发送事件2
线程2 :接收到事件2EVENT_FLAG2,执行线程2,并发送事件3
线程3 :接收到事件3EVENT_FLAG3,执行线程3,并发送事件1
三个事件循环发生,所以依次执行线程1,2,3
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
rt_thread_t th1 = NULL,th2 = NULL,th3 = NULL;
rt_event_t event;
// 事件集创建flag,建议采用 RT_IPC_FLAG_PRIO,即确保线程的实时性。
//#define RT_IPC_FLAG_FIFO 0x00 /**< FIFOed IPC. @ref IPC. */
//#define RT_IPC_FLAG_PRIO 0x01 /**< PRIOed IPC. @ref IPC. */
//事件集接收timeout
//#define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */
//#define RT_WAITING_NO 0 /**< Non-block. */
//该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理
//事件集接收option ,选择 逻辑与 或 逻辑或 的方式接收事件
RT_EVENT_FLAG_OR
RT_EVENT_FLAG_AND
选择清除重置事件标志位
RT_EVENT_FLAG_CLEAR
//事件定义
#define EVENT_FLAG1 (1 << 0)
#define EVENT_FLAG2 (1 << 1)
#define EVENT_FLAG3 (1 << 2)
/**
* rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
// set 表示事件类型标志,option表示逻辑与 或 逻辑或 的方式接收事件
timeout表示接收的等待时间,永久等待还是不等待
recved表示接收的事件类型
rt_err_t rt_event_recv(rt_event_t event,
rt_uint32_t set,
rt_uint8_t option,
rt_int32_t timeout,
rt_uint32_t* recved); //recved用于接收发生的事件
*/
void th1_entry(void *parameter)
{
rt_uint32_t value = 0,re_value;
value = (rt_uint32_t)parameter; //这里parameter是4字节,所以不能用rt_uint8_t转换
while(1)
{
rt_event_recv(event,EVENT_FLAG1,RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
RT_WAITING_FOREVER, &re_value );
rt_kprintf("receive event 0x%x...\n",re_value);
rt_kprintf("thread %d is runing...\n",value);
rt_kprintf("\n");
rt_event_send(event,EVENT_FLAG2);
rt_thread_mdelay(1000); //
}
}
void th2_entry(void *parameter)
{
rt_uint32_t value = 0,re_value;
value = (rt_uint32_t)parameter; //这里parameter是4字节,所以不能用rt_uint8_t转换
while(1)
{
rt_event_recv(event,EVENT_FLAG2,RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
RT_WAITING_FOREVER, &re_value );
rt_kprintf("receive event 0x%x...\n",re_value);
rt_kprintf("thread %d is runing...\n",value);
rt_kprintf("\n");
rt_event_send(event,EVENT_FLAG3);
rt_thread_mdelay(1000); //
}
}
void th3_entry(void *parameter)
{
rt_uint32_t value = 0,re_value;
value = (rt_uint32_t)parameter; //这里parameter是4字节,所以不能用rt_uint8_t转换
while(1)
{
rt_event_recv(event,EVENT_FLAG3,RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
RT_WAITING_FOREVER, &re_value );
rt_kprintf("receive event 0x%x...\n",re_value);
rt_kprintf("thread %d is runing...\n",value);
rt_kprintf("\n");
rt_event_send(event,EVENT_FLAG1);
rt_thread_mdelay(1000); //
}
}
int main(void)
{
//创建事件
event = rt_event_create("event",RT_IPC_FLAG_PRIO);
//创建两个线程用来访问临界区域
th1 = rt_thread_create("th1", th1_entry, (void*)1, 512, 10, 5);
if(th1 == RT_NULL){
LOG_E("th1 rt_thread_create failed...\n");
return RT_ENOMEM;
}
LOG_D("th1 rt_thread_create successed...\n");
rt_thread_startup(th1);
th2 = rt_thread_create("th2", th2_entry, (void*)2, 512, 9, 5);
if(th2 == RT_NULL){
LOG_E("th2 rt_thread_create failed...\n");
return RT_ENOMEM;
}
LOG_D("th2 rt_thread_create successed...\n");
rt_thread_startup(th2);
th3 = rt_thread_create("th3", th3_entry, (void*)3, 512, 8, 5);
if(th3 == RT_NULL){
LOG_E("th3 rt_thread_create failed...\n");
return RT_ENOMEM;
}
LOG_D("th3 rt_thread_create successed...\n");
rt_thread_startup(th3);
rt_event_send(event,EVENT_FLAG1);
return RT_EOK;
}
运行结果:
[2022-10-26_18:13:36:955]receive event 0x1...
[2022-10-26_18:13:36:955]thread 1 is runing...
[2022-10-26_18:13:36:955]receive event 0x2...
[2022-10-26_18:13:36:970]thread 2 is runing...
[2022-10-26_18:13:36:970]receive event 0x4...
[2022-10-26_18:13:36:970]thread 3 is runing...
[2022-10-26_18:13:36:970]msh >receive event 0x1...
[2022-10-26_18:13:37:974]thread 1 is runing...
[2022-10-26_18:13:37:974]receive event 0x2...
[2022-10-26_18:13:37:974]thread 2 is runing...
[2022-10-26_18:13:37:974]receive event 0x4...
[2022-10-26_18:13:37:974]thread 3 is runing...