事件#
事件集(Event):#
事件集可以看作公交⻋站的场景。线程可以等待多个事件发⽣,⽐如等待公交⻋或等待同伴到达。如果满⾜某个或某⼏个条件,线程将被唤醒继续执⾏
1. rt_event_create
用于动态创建一个事件对象。
rt_event_t rt_event_create(const char *name, rt_uint8_t flag);参数说明:
name:事件对象的名字(可以为RT_NULL表示匿名事件对象)。
flag:IPC 对象的属性标志,一般使用RT_IPC_FLAG_PRIO表示优先级等待,或RT_IPC_FLAG_FIFO表示先进先出等待。返回值:
成功:返回事件对象的句柄
rt_event_t。失败:返回
RT_NULL。示例:
rt_event_t event = rt_event_create("event", RT_IPC_FLAG_PRIO);2. rt_event_init
用于静态初始化事件对象。一般用于系统或内存已静态分配的场景。
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag);参数说明:
event:事件对象指针(需要预先定义事件对象结构体)。
name:事件对象名字。
flag:IPC 对象的属性标志,和rt_event_create一样,通常使用RT_IPC_FLAG_PRIO或RT_IPC_FLAG_FIFO。返回值:
成功:返回
RT_EOK。失败:返回相应的错误代码。
示例:
/* 静态事件对象定义 */ static struct rt_event static_event; /* 初始化静态事件对象 */ rt_event_init(&static_event, "static_event", RT_IPC_FLAG_PRIO);3. rt_event_delete
用于删除动态创建的事件对象(
rt_event_create创建的事件对象)。rt_err_t rt_event_delete(rt_event_t event);参数说明:
event:事件对象的句柄。返回值:
成功:返回
RT_EOK。失败:返回相应的错误代码。
示例:
rt_event_delete(event); // 删除动态创建的事件对象4. rt_event_detach
用于卸载静态初始化的事件对象(
rt_event_init初始化的事件对象)。rt_err_t rt_event_detach(rt_event_t event);参数说明:
event:事件对象的句柄。返回值:
成功:返回
RT_EOK。失败:返回相应的错误代码。
示例:
rt_event_detach(&static_event); // 卸载静态初始化的事件对象5. rt_event_send
用于发送(触发)一个事件,设置指定的事件标志位。线程会根据其等待的事件标志来判断是否继续执行。
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);参数说明:
event:事件对象的句柄。
set:事件标志,设置哪几位事件标志被触发(可以用1 << n来表示第 n 位的事件)。返回值:
成功:返回
RT_EOK。失败:返回相应的错误代码。
示例:
rt_event_send(event, (1 << 3)); // 触发位3的事件6. rt_event_recv
用于接收(等待)事件,线程可以等待多个事件标志。当等待的事件标志满足条件后,线程被唤醒继续执行。
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);参数说明:
event:事件对象的句柄。
set:需要等待的事件标志(例如1 << 3 | 1 << 5表示同时等待第3位和第5位的事件)。
option:等待选项,常见选项包括:
RT_EVENT_FLAG_AND:所有指定的事件标志都满足时才唤醒线程。
RT_EVENT_FLAG_OR:只要有一个指定的事件标志满足就唤醒线程。
RT_EVENT_FLAG_CLEAR:收到事件标志后清除这些事件标志。
timeout:超时时间(单位为系统 tick),可以为RT_WAITING_FOREVER表示永远等待。
recved:输出参数,返回实际接收到的事件标志。返回值:
成功:返回
RT_EOK。失败:返回
-RT_ETIMEOUT表示超时,或其他错误代码。示例:
rt_uint32_t recved; rt_event_recv(event, (1 << 3 | 1 << 5), RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &recved);总结:
rt_event_create:用于动态创建事件对象。
rt_event_init:用于静态初始化事件对象。
rt_event_delete:用于删除动态事件对象。
rt_event_detach:用于卸载静态初始化的事件对象。
rt_event_send:发送事件,触发指定的事件标志位。
rt_event_recv:接收事件,线程根据等待条件阻塞,直到指定事件发生。
注意动态和静态创建的事件要用不同函数操作
事件集的⼯作机制:#
事件集⽤于线程间的同步,可以让线程等待⼀个或多个事件的触发:
逻辑与(AND):线程等待多个事件同时发⽣才被唤醒。
逻辑或(OR):线程只需等待其中⼀个事件发⽣即可被唤醒。
线程1等待公交⻋3或公交⻋5的到来,公交⻋到站时线程被唤醒。
线程2模拟公交⻋3到站,发送事件,唤醒等待的线程。
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-02-06 RT-Thread first version
*/
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
// 定义一个事件对象指针
rt_event_t event = RT_NULL;
// 线程1的入口函数
void thread1_entry1(void *parameter)
{
// 定义一个变量,用于存储接收到的事件值
rt_uint32_t received;
// 输出线程1正在等待的公交车信息
rt_kprintf("线程1等待公交车3或5\n");
// 线程1等待事件
// 等待事件3或事件5,使用或操作(RT_EVENT_FLAG_OR)和清除标志(RT_EVENT_FLAG_CLEAR)
// 如果事件发生,清除接收到的事件
rt_event_recv(event, (1 << 3 | 1 << 5), RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &received);
// 输出线程1接收到的事件
rt_kprintf("线程1的第%d路公交车到了,出发\n", received);
// 线程1延迟1秒
rt_thread_mdelay(1000);
}
// 线程2的入口函数
void thread2_entry2(void *parameter)
{
// 向事件对象发送事件3
rt_event_send(event, (1 << 3));
// 输出线程2发送的事件
rt_kprintf("线程1的第3路公交车进站\n");
}
// 主函数
int main(void)
{
// 创建事件对象,名称为"event"
event = rt_event_create("event", RT_IPC_FLAG_PRIO);
// 创建线程1
rt_thread_t thread1 = rt_thread_create("thread1", thread1_entry1, RT_NULL, 1024, 25, 10);
// 创建线程2
rt_thread_t thread2 = rt_thread_create("thread2", thread2_entry2, RT_NULL, 1024, 25, 10);
// 如果线程1创建成功,启动线程1
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
// 如果线程2创建成功,启动线程2
if (thread2 != RT_NULL)
{
rt_thread_startup(thread2);
}
// 主函数返回0,表示正常结束
return 0;
}
实现效果:

裁判1负责发出“准备”信号,裁判2负责发出“开始”信号。运动员需要等到两个信号都收到后才能起跑。
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-02-06 RT-Thread first version
*/
#include <rtthread.h> // 包含RT-Thread的核心头文件
#define DBG_TAG "main" // 定义日志标签
#define DBG_LVL DBG_LOG // 定义日志级别为普通日志
#include <rtdbg.h> // 包含调试日志的头文件
rt_event_t event = RT_NULL; // 定义一个事件对象指针,初始化为NULL
// 运动员线程,等待两个裁判的信号
void athlete_entry(void *parameter)
{
rt_uint32_t received; // 定义变量,用于存储接收到的事件标志
// 输出日志:等待裁判的准备和开始信号
rt_kprintf("等待裁判的准备和开始信号\n");
// 等待事件
// 事件标志:裁判1(1<<1)和裁判2(1<<2)
// 等待标志:RT_EVENT_FLAG_AND(必须同时满足两个信号)和 RT_EVENT_FLAG_CLEAR(清除接收到的信号)
// 等待时间:无限等待(RT_WAITING_FOREVER)
// 接收到的事件标志存储在received变量中
rt_event_recv(event, (1 << 1 | 1 << 2), RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &received);
// 输出日志:运动员开跑
rt_kprintf("运动员开跑\n");
}
// 裁判1线程,发送开始信号
void referee1_entry(void *parameter)
{
// 输出日志:裁判1发送开始信号
rt_kprintf("裁判1发送开始信号\n");
// 发送事件给事件对象,设置裁判1的信号标志(1<<1)
rt_event_send(event, (1 << 1));
// 线程延迟500毫秒
rt_thread_mdelay(500);
}
// 裁判2线程,发送准备信号
void referee2_entry(void *parameter)
{
// 输出日志:裁判2发送准备信号
rt_kprintf("裁判2发送准备信号\n");
// 发送事件给事件对象,设置裁判2的信号标志(1<<2)
rt_event_send(event, (1 << 2));
// 线程延迟500毫秒
rt_thread_mdelay(500);
}
// 主函数
int main(void)
{
// 创建事件对象,名称为"event"
event = rt_event_create("event", RT_IPC_FLAG_PRIO);
// 创建运动员线程
rt_thread_t thread1 = rt_thread_create("thread1", athlete_entry, RT_NULL, 1024, 25, 10);
// 创建裁判2线程
rt_thread_t thread2 = rt_thread_create("thread2", referee2_entry, RT_NULL, 1024, 25, 10);
// 创建裁判1线程
rt_thread_t thread3 = rt_thread_create("thread3", referee1_entry, RT_NULL, 1024, 25, 10);
// 如果线程1创建成功,启动线程1
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
// 如果线程2创建成功,启动线程2
if (thread2 != RT_NULL)
{
rt_thread_startup(thread2);
}
// 如果线程3创建成功,启动线程3
if (thread3 != RT_NULL)
{
rt_thread_startup(thread3);
}
// 主函数返回0,表示正常结束
return 0;
}
实现效果:
