RT-Thread事件集原理与函数说明及代码示例

1、 事件集原理

事件集主要用于线程间同步,主要的构成是由一个32位的无符号整形的某位来进行事件接收与发送,它的特点是可以一对一、一对多、多对一,举个B站的例子:当前你在公交站等车,今天只有一辆公交车等够让你到达目的地,这是一对一;这时条件变了,今天能让你到达目的地又加了一辆,有两辆公交车能让你到达目的地,这是一对多;多对一的情况就是你只有等到了你的朋友,你俩只有在公交站碰头之后才能坐上这辆公交车。(需要注意的是,多对多也可以实现哦)
RT-Thread定义的事件有以下特点:
• 事件只与线程相关,事件间相互独立:每个线程拥有32个事件标志,采用一个32 bit无符号整型数进行记录,每一个bit代表一个事件。若干个事件构成一个事件集;
• 事件仅用于同步,不提供数据传输功能;
• 事件无排队性,即多次向线程发送同一事件(如果线程还未来得及读走),其效果等同于只发送一次。

在RT-Thread实现中,每个线程都拥有一个事件信息标记,它有三个属性,分别是
RT_EVENT_FLAG_AND(逻辑与),RT_EVENT_FLAG_OR(逻辑或)以及RT_EVENT_FLAG_CLEAR(清除标记)。当线程等待事件同步时,可以通过32个事件标志和这个事件信息标记来判断当前接收的事件是否满足同步条件。

事件控制块定义如下

struct rt_event
{
	struct rt_ipc_object parent; /* 继承自ipc_object类 */
	rt_uint32_t set; /* 事件集合 */
};
/* rt_event_t是指向事件结构体的指针 */
typedef struct rt_event* rt_event_t;
rt_event对象从rt_ipc_object 中派生,由IPC容器管理。

2、 函数说明

创建及删除事件
re_event_t rt_event_create(const char
name,rt_uint8_t flag)
rt_err_t rt_event_delete(re_event_t event)
*

re_event_create从动态内存堆中创建动态事件,进行对象初始化,IPC初始化,将set置为0,flag可选两个参数如下:
#define RT_IPC_FLAG_FIFO 0x00 /* IPC参数采用FIFO方式*/
#define RT_IPC_FLAG_PRIO 0x01 /* IPC参数采用优先级方式*/
创建成功返回任务句柄,创建失败返回RT_NULL
rt_event_delete释放动态创建会唤起所有挂在该事件上等待的线程块,并将接收事件返回-RT_ERROR

*初始化事件及脱离时间
rt_err_t rt_event_init(rt_event_t event,const char name,rt_uint8_t flag)
rt_err_t rt_event_detach(rt_event_t event

rt_event_init静态事件的内存是在编译时期由编译器分配的,一般存放在数据段或ZI段中,flag定义如下
#define RT_IPC_FLAG_FIFO 0X00 /IPC参数采用FIFO方式/
#define RT_IPC_FLAG_PRIO 0X01 /IPC参数采用优先级方式/
函数返回RT_EOK
rt_event_detach脱离事件是将事件对象从内核管理列表中删除

*接收事件
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)

内核使用32位的无符号整型数来标识事件,它的每一位代表一个事件,因此一个事件对象可同时等待接收32个事件,内核可以通过指定选择参数“逻辑与”或“逻辑或”来选择如何激活线程,使用“逻辑与”参数表示只有当所有等待的事件都发生时才激活线程,而使用“逻辑或”参数则表示只要有一个等待的事件发生就激活线程
当用户调用这个接口时,系统首先根据set参数和接收选项来判断它要接收的事件是否发生,如果已经发生,则根据参数option上是否设置有RT_EVENT_FLAG_CLEAR来决定是否重置事件的相应标志位,然后返回(其中recved参数返回收到的事件); 如果没有发生,则把等待的set和option参数填入线程本身的结构中,然后把线程挂起在此事件对象上,直到其等待的事件满足条件或等待时间超过指定的超时时间。如果超时时间设置为零,则表示当线程要接受的事件没有满足其要求时就不等待,而直接返回-RT_ETIMEOUT。
参数 描述
event 事件对象的句柄。
set 接收线程感兴趣的事件;
option 接收选项;
timeout 指定超时时间;
recved 指向收到的事件;
函数返回
正确接收返回RT_EOK,超时返回-RT_ETIMEOUT,其他返回-RT_ERROR。

发送事件
通过发送事件服务,可以发送一个或多个事件。发送事件可以使用下面的函数接口:
rt_err_t rt_event_send(rt_event_t event,rt_uint32_t set)

使用该函数接口时,通过参数set指定的事件标志来设定event对象的事件标志值,然后遍历等待在event事件对象上的等待线程链表,判断是否有线程的事件激活要求与当前event对象事件标志值匹配,如果有,则唤醒该线程。
参数 描述
event 事件对象的句柄。
set 发送的事件集;
函数返回
RT_EOK

3、代码示例

/*
* 程序清单:事件例程
*
* 这个程序会创建3个动态线程及初始化一个静态事件对象
* 一个线程等待在事件对象上以接收事件;
* 一个线程定时发送事件 (事件3)
* 一个线程定时发送事件 (事件5)
*/
#include <rtthread.h>
#include "tc_comm.h"
/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static rt_thread_t tid3 = RT_NULL;
/* 事件控制块 */
static struct rt_event event;
/* 线程1入口函数 */
static void thread1_entry(void *param)
{
	rt_uint32_t e;
	while (1)
	{
		/* 以逻辑与的方式接收事件 */
		if (rt_event_recv(&event, ((1 << 3) | (1 << 5)),
		RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
		RT_WAITING_FOREVER, &e) == RT_EOK)
		{
			rt_kprintf("thread1: AND recv event 0x%x\n", e);
		}
		rt_kprintf("thread1: delay 1s to prepare second event\n");
		rt_thread_delay(10);
		/* 以逻辑或的方式接收事件 */
		if (rt_event_recv(&event, ((1 << 3) | (1 << 5)),
		RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
		RT_WAITING_FOREVER, &e) == RT_EOK)
		{
			rt_kprintf("thread1: OR recv event 0x%x\n", e);
		}
		rt_thread_delay(5);
	}
}
/* 线程2入口函数 */
static void thread2_entry(void *param)
{
	/* 线程2持续地发送事件#3 */
	while (1)
	{
		rt_kprintf("thread2: send event1\n");
		rt_event_send(&event, (1 << 3));
		rt_thread_delay(10);
	}
}
/* 线程3入口函数 */
static void thread3_entry(void *param)
{
	/* 线程3持续地发送事件#5 */
	while (1)
	{
		rt_kprintf("thread3: send event2\n");
		rt_event_send(&event, (1 << 5));
		rt_thread_delay(20);
	}
}
int event_simple_init()
{
	/* 初始化事件对象 */
	rt_event_init(&event, "event", RT_IPC_FLAG_FIFO);
	/* 创建线程1 */
	tid1 = rt_thread_create("t1",
	thread1_entry, /* 线程入口是thread1_entry */
	RT_NULL, /* 入口参数是RT_NULL */
	THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
	if (tid1 != RT_NULL)
		rt_thread_startup(tid1);
	else
		tc_stat(TC_STAT_END | TC_STAT_FAILED);
	/* 创建线程2 */
	tid2 = rt_thread_create("t2",
	thread2_entry, /* 线程入口是thread2_entry */
	RT_NULL, /* 入口参数是RT_NULL */
	THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
	if (tid2 != RT_NULL)
		rt_thread_startup(tid2);
	else
		tc_stat(TC_STAT_END | TC_STAT_FAILED);
	/* 创建线程3 */
	tid3 = rt_thread_create("t3",
	thread3_entry, /* 线程入口是thread3_entry */
	RT_NULL, /* 入口参数是RT_NULL */
	THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
	if (tid3 != RT_NULL)
		rt_thread_startup(tid3);
	else
		tc_stat(TC_STAT_END | TC_STAT_FAILED);
	return 0;
}
#ifdef RT_USING_TC
static void _tc_cleanup()
{
	/* 调度器上锁,上锁后,将不再切换到其他线程,仅响应中断 */
	rt_enter_critical();
	/* 删除线程 */
	if (tid1 != RT_NULL && tid1->stat != RT_THREAD_CLOSE)
		rt_thread_delete(tid1);
	if (tid2 != RT_NULL && tid2->stat != RT_THREAD_CLOSE)
		rt_thread_delete(tid2);
	if (tid3 != RT_NULL && tid3->stat != RT_THREAD_CLOSE)
		rt_thread_delete(tid3);
	/* 执行事件对象脱离 */
	rt_event_detach(&event);
	/* 调度器解锁 */
	rt_exit_critical();
	/* 设置TestCase状态 */
	tc_done(TC_STAT_PASSED);
}
int _tc_event_simple()
{
	/* 设置TestCase清理回调函数 */
	tc_cleanup(_tc_cleanup);
	event_simple_init();
	/* 返回TestCase运行的最长时间 */
	return 100;
}
/* 输出函数命令到finsh shell中 */
FINSH_FUNCTION_EXPORT(_tc_event_simple, a simple event example);
#else
/* 用户应用入口 */
int rt_application_init()
{
	event_simple_init();
	return 0;
}
#endif

猜你喜欢

转载自blog.csdn.net/weixin_42560250/article/details/105671168