事件#

事件集(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_PRIORT_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;
}

实现效果: image-20250207141625489

裁判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;
}

实现效果: image-20250207144050660