前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >超精简的订阅发布事件组件--SPEvent

超精简的订阅发布事件组件--SPEvent

作者头像
Rice加饭
修改于 2023-03-24 01:16:00
修改于 2023-03-24 01:16:00
44200
代码可运行
举报
文章被收录于专栏:Rice嵌入式Rice嵌入式
运行总次数:0
代码可运行

概述

  • 本文主要描述一个超精简的订阅发布事件组件--SPEvent。
  • 在实际开发过程中,一个事件的产生会产生很多业务的执行,或者多个事件都要执行同一个业务的执行。在这种场景下有两种做法:
    1. 将同一个事件的业务放在一个函数中,然后事件产生的时候执行对应的函数。
    2. 某个业务需要哪个事件,它自己监听对应事件并执行。
  • 显然,第一种策略会将业务与业务之间耦合在一起,对后期维护是非常痛苦的;第二种显然会更加有优势,不同业务完全解耦,独立完成事件的业务。
  • 第二种策略的方式,实际在软件架构中经常看到,比如MQTT的通信(通过订阅对应的topic去监听对应内容)。
  • 有了上述的需求,作者做了一个超精简的订阅发布事件组件。整个逻辑很简单。

超精简的SPEvent组件,实现方法

  1. 整个订阅发布事件机制,引入两个东西:EventHub和EventNode。
    • EventHub:每一个事件类型都为一个EventHub,然后挂在HubList中。
    • EventNode:每一个订阅事件的业务为一个EventNode,然后挂在对应的EventHub中。
  2. 整个订阅发布事件机制围绕着EventHub和EventNode,特点:
    • 资源占用极小,接口操作简单
    • 事件支持动态订阅,动态注销。
  3. SPEvent采用双向链表进行维护整个订阅-发布逻辑
    • SPEvent一定存在一个EventHubList链表来维护事件类型,它默认是没有任何EventHub节点,
    • 订阅事件流程:当订阅者订阅事件之后,如果事件不存在,则申请一个EventHub,并将EventHub挂在到EventHubList链表中;然后申请一个EventNode,及将对应EventNode挂在EventNodeList链表。
    • 发布事件流程:当发布者发布事件时,会从EventHubList中查询有没有对应的EventHub,如果EventHub存在,则将事件消息发布给对应EventHub下所有EventNode。
    • 注销事件订阅流程:当订阅者注销已经订阅的事件,会从EventHubList中查询有没有对应的EventHub,如果EventHub存在,则将对应EventNode从EventHub中删除。

超精简的SPEvent组件,接口说明:

函数

说明

SPEventInit

初始化函数

SPEventDeinit

去初始化函数

SPEventSubscribe

订阅事件函数

SPEventUnsubscribe

注销订阅事件函数

SPEventPublish

发布事件消息

SPEventClear

清除事件池

RecvtInfoDump

导出事件池信息

超精简的SPEvent组件,代码实现

  1. 整个代码接口存在3个文件:spevent.c、spevent.h、spevent_def.h。其中:
  2. spevent_def.h文件说明:定义了屏蔽平台相关接口的宏和定义了双向量表操作的宏定义。双向量表在SPEvent中式至关重要数据结构
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifndef __SPEVENT_DEF_H__
#define __SPEVENT_DEF_H__



#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#define SPEVENT_INLINE                           static __inline
#define SPEVENT_EVENT_NAME_LEN                   16

#ifndef SPEVENT_MALLOC
    #define SPEVENT_MALLOC                       malloc
#endif

#ifndef SPEVENT_FREE
    #define SPEVENT_FREE                         free
#endif

#ifndef SPEVENT_PRINT
    #define SPEVENT_PRINT                        printf
#endif

struct SPEventListNode
{
    struct SPEventListNode *next;
    struct SPEventListNode *prev;
};
typedef struct SPEventListNode SPEventList;

SPEVENT_INLINE void SPEVENT_LIST_INIT(SPEventList *l)
{
    l->next = l->prev = l;
}

SPEVENT_INLINE void SPEVENT_LIST_INSERT_AFTER(SPEventList *l, SPEventList *n)
{
    l->next->prev = n;
    n->next = l->next;
    l->next = n;
    n->prev = l;
}

SPEVENT_INLINE void SPEVENT_LIST_INSERT_BEFORE(SPEventList *l, SPEventList *n)
{
    l->prev->next = n;
    n->prev = l->prev;
    l->prev = n;
    n->next = l;
}

SPEVENT_INLINE void SPEVENT_LIST_REMOVE(SPEventList *n)
{
    n->next->prev = n->prev;
    n->prev->next = n->next;
    n->next = n->prev = n;
}

SPEVENT_INLINE int SPEVENT_LIST_LEN(const SPEventList *l)
{
    int len = 0;
    const SPEventList *p = l;
    while (p->next != l) {
        p = p->next;
        len ++;
    }
    return len;
}

#define SPEVENT_CONTAINER_OF(ptr, type, member)                                  \
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

#define SPEVENT_LIST_OBJ_INIT(obj) {&(obj), &(obj)}

#define SPEVENT_LIST_ENTRY(node, type, member)                                   \
    SPEVENT_CONTAINER_OF(node, type, member)

#define SPEVENT_LIST_FOR_EACH(pos, head)                                         \
    for (pos = (head)->next; pos != (head); pos = pos->next)

#define SPEVENT_LIST_FOR_EACH_SAFE(pos, n, head)                                 \
    for (pos = (head)->next, n = pos->next; pos != (head);                       \
        pos = n, n = pos->next)

#define SPEVENT_LIST_FOR_EACH_ENTRY(pos, head, member)                           \
    for (pos = SPEVENT_LIST_ENTRY((head)->next, typeof(*pos), member);           \
         &pos->member != (head);                                                 \
         pos = SPEVENT_LIST_ENTRY(pos->member.next, typeof(*pos), member))

#define SPEVENT_LIST_FOR_EACH_ENTRY_SAFE(pos, n, head, member)                   \
    for (pos = SPEVENT_LIST_ENTRY((head)->next, typeof(*pos), member),           \
         n = SPEVENT_LIST_ENTRY(pos->member.next, typeof(*pos), member);         \
         &pos->member != (head);                                                 \
         pos = n, n = SPEVENT_LIST_ENTRY(n->member.next, typeof(*n), member))

#define SPEVENT_LIST_FIRST_ENTRY(ptr, type, member)                              \
    SPEVENT_LIST_ENTRY((ptr)->next, type, member)

#endif
  1. spevent.c文件说明:SPEvent的接口实现;整个逻辑通过链表的嵌套,实现了事件的管理,事件的订阅,事件的发布。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "spevent.h"

static SPEventList g_hublist = {0};

static SPEventHubNode *SPEventFindHubNode(SPEventList *list, const char *event)
{
    SPEventHubNode *hubNode = NULL;
    SPEventList *node = NULL;

    SPEVENT_LIST_FOR_EACH(node, list) {
        hubNode = SPEVENT_LIST_ENTRY(node, SPEventHubNode, hubList);
        if(hubNode != NULL && strcmp(hubNode->event, event) == 0) {
            return hubNode;
        }
    }
    return NULL;
}

static SPEventEventNode *SPEventFindEventNode(SPEventList *eventList, SPEventHandle handle)
{
    SPEventEventNode *eventNode = NULL;
    SPEventList *node = NULL;

    SPEVENT_LIST_FOR_EACH(node, eventList) {
        eventNode = SPEVENT_LIST_ENTRY(node, SPEventEventNode, list);
        if(eventNode != NULL && handle == eventNode->handle) {
            return eventNode;
        }
    }
    return NULL;
}

SPEventEventNode *SPEventSubscribe(const char *event, SPEventHandle handle)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;

    hubNode = SPEventFindHubNode(&g_hublist, event);
    if(hubNode == NULL) {
        hubNode = (SPEventHubNode *)SPEVENT_MALLOC(sizeof(SPEventHubNode));
        if(hubNode == NULL) {
            SPEVENT_PRINT("SPEVENT hub(%s) malloc failed\r\n", event);
            return NULL;
        }
        memset(hubNode->event, 0, SPEVENT_EVENT_NAME_LEN);
        memcpy(hubNode->event, event, strlen(event));
        SPEVENT_LIST_INSERT_AFTER(&g_hublist, &(hubNode->hubList));
        SPEVENT_LIST_INIT(&(hubNode->eventList));
    }

    eventNode = (SPEventEventNode *)SPEVENT_MALLOC(sizeof(SPEventEventNode));
    if(eventNode == NULL) {
        SPEVENT_PRINT("SPEVENT event(%s) malloc failed\r\n", event);
        return NULL;
    }
    eventNode->handle = handle;
    SPEVENT_LIST_INSERT_AFTER(&hubNode->eventList, &(eventNode->list));

    SPEVENT_PRINT("SPEVENT event(%s) Subscribe success\r\n", event);
    return eventNode;
}

bool SPEventUnsubscribe(const char *event, SPEventEventNode *node)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;

    hubNode = SPEventFindHubNode(&g_hublist, event);
    if(hubNode == NULL) {
        SPEVENT_PRINT("SPEVENT hub(%s) find failed\r\n", event);
        return false;
    }
    eventNode = SPEventFindEventNode(&(hubNode->eventList), node->handle);
    if(eventNode == NULL) {
        SPEVENT_PRINT("SPEVENT event(%s) find failed\r\n", event);
        return false;
    }
    SPEVENT_LIST_REMOVE(&(eventNode->list));
    SPEVENT_FREE(eventNode);
    eventNode = NULL;

    SPEVENT_PRINT("SPEVENT event(%s) Unsubscribe success\r\n", event);
    return true;
}

bool SPEventPublish(const char *event, void *payload)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;
    SPEventList *node = NULL;

    hubNode = SPEventFindHubNode(&g_hublist, event);
    if(hubNode == NULL) {
        SPEVENT_PRINT("SPEVENT hub(%s) find failed\r\n");
        return false;
    }
    SPEVENT_LIST_FOR_EACH(node, &(hubNode->eventList)) {
        eventNode = SPEVENT_LIST_ENTRY(node, SPEventEventNode, list);
        if(eventNode->handle) {
            eventNode->handle(event, payload);
        }
    }

    SPEVENT_PRINT("SPEVENT event(%s) Publish success\r\n", event);
    return true;
}

void SPEventClear(void)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;
    SPEventList *hubList = NULL;
    SPEventList *eventList = NULL;

    SPEVENT_LIST_FOR_EACH(hubList, &g_hublist) {
        hubNode = SPEVENT_LIST_ENTRY(hubList, SPEventHubNode, hubList);
        if(hubNode == NULL) {
            continue;
        }
        SPEVENT_LIST_FOR_EACH(eventList, &(hubNode->eventList)) {
            eventNode = SPEVENT_LIST_ENTRY(eventList, SPEventEventNode, list);
            if(eventNode == NULL) {
                continue;
            }
            SPEVENT_LIST_REMOVE(&(eventNode->list));
            SPEVENT_FREE(eventNode);
            eventNode = NULL;
        }

        SPEVENT_LIST_REMOVE(&(hubNode->hubList));
        SPEVENT_FREE(hubNode);
        hubNode = NULL;
    }
}

void RecvtInfoDump(void)
{
    SPEventHubNode *hubNode = NULL;
    SPEventEventNode *eventNode = NULL;
    SPEventList *hubList = NULL;
    SPEventList *eventList = NULL;
    int eventNodeCount = 0;

    SPEVENT_PRINT("SPEVENT list: \r\n");

    SPEVENT_LIST_FOR_EACH(hubList, &g_hublist) {
        hubNode = SPEVENT_LIST_ENTRY(hubList, SPEventHubNode, hubList);
        if(hubNode == NULL) {
            continue;
        }
        SPEVENT_PRINT("SPEVENT event(%s): ", hubNode->event);
        eventNodeCount = 0;
        SPEVENT_LIST_FOR_EACH(eventList, &(hubNode->eventList)) {
            eventNode = SPEVENT_LIST_ENTRY(eventList, SPEventEventNode, list);
            if(eventNode == NULL) {
                continue;
            }
            eventNodeCount++;
        }
        SPEVENT_PRINT("%d\r\n", eventNodeCount);
    }
}

void SPEventInit(void)
{
    SPEVENT_LIST_INIT(&g_hublist);
}

void SPEventDeinit(void)
{
    SPEventClear();
}
  1. spevent.h文件说明:SPEvent的接口申明及SPEvent相关接口体的定义。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifndef __SPEVENT_H__
#define __SPEVENT_H__

#include "spevent_def.h"
typedef void (*SPEventHandle)(const char *event, void *payload);

typedef struct
{
    SPEventHandle handle;
    SPEventList list;
}SPEventEventNode;

typedef struct
{
    char event[SPEVENT_EVENT_NAME_LEN];
    SPEventList eventList;
    SPEventList hubList;
}SPEventHubNode;

void SPEventInit(void);

void SPEventDeinit(void);

SPEventEventNode *SPEventSubscribe(const char *event, SPEventHandle handle);

bool SPEventUnsubscribe(const char *event, SPEventEventNode *node);

bool SPEventPublish(const char *event, void *payload);

void SPEventClear(void);

void RecvtInfoDump(void);

#endif

超精简的SPEvent组件,实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void SPEventHandle1(const char *event, void *payload)
{
    SPEVENT_PRINT("Event1: %s, payload: %s", event, payload);
}

void SPEventHandle2(const char *event, void *payload)
{
    SPEVENT_PRINT(ent2: %s, payload: %s", event, payload);
}

int main()
{
    SPEventInit();
 
    SPEventEventNode *eventNode1 = SPEventSubscribe("Rice", SPEventHandle1);
    if(eventNode1 != NULL)
    {
        SPEventPublish("Rice", "hello");
    } else {
        rlog_e("SPEVENT subscribe event failed");
    }

    SPEventEventNode *eventNode2 = SPEventSubscribe("Rice", SPEventHandle2);
    if(eventNode2 != NULL)
    {
        SPEventPublish("Rice", "world");
    } else {
        rlog_e("SPEVENT subscribe event failed");
    }

    RecvtInfoDump();

    SPEventUnsubscribe("Rice", eventNode1);

    SPEventPublish("Rice", "Hello world");

    RecvtInfoDump();
    return 0;
}

  • 结果:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SPEVENT event(Rice) Subscribe success
Event1: Rice, payload: hello
SPEVENT event(Rice) Publish success
SPEVENT event(Rice) Subscribe success
Event2: Rice, payload: world
Event1: Rice, payload: world
SPEVENT event(Rice) Publish success
SPEVENT list: 
SPEVENT event(Rice): 2
SPEVENT event(Rice) Unsubscribe success
Event2: Rice, payload: Hello world
SPEVENT event(Rice) Publish success
SPEVENT list: 
SPEVENT event(Rice): 1
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-01-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 超精简的SPEvent组件,实现方法
  • 超精简的SPEvent组件,接口说明:
  • 超精简的SPEvent组件,代码实现
  • 超精简的SPEvent组件,实例:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档