当前位置:

首页 > 编程开发 > C++事件总线设计:发布订阅模式详解

C++事件总线设计:发布订阅模式详解

事件总线是一种解耦发布者与订阅者的通信机制,通过维护事件类型到订阅者列表的映射实现事件分发。1.使用typeid获取事件类型并分发事件;2.事件内存由事件总线在publish后释放;3.避免性能瓶颈可采用异步处理、事件过滤、分层事件总线、线程池等策略;4.处理异常可通过try-catch块、错误处理函数或返回值机制;5.保证事件顺序可通过单线程处理、序列化到消息队列或使用版本号控制。

事件总线是一种解耦发布者与订阅者的通信机制,通过维护事件类型到订阅者列表的映射实现事件分发。1. 使用typeid获取事件类型并分发事件;2. 事件内存由事件总线在publish后释放;3. 避免性能瓶颈可采用异步处理、事件过滤、分层事件总线、线程池等策略;4. 处理异常可通过try-catch块、错误处理函数或返回值机制;5. 保证事件顺序可通过单线程处理、序列化到消息队列或使用版本号控制。

C++中如何设计事件总线_发布订阅模式

事件总线/发布订阅模式,简单来说,就是让不同的对象(发布者和订阅者)在不知道彼此的情况下进行通信。发布者发出事件,订阅者接收并处理感兴趣的事件。这是一种解耦的好方法,能让系统更灵活、可扩展。

C++中如何设计事件总线_发布订阅模式

解决方案

C++中如何设计事件总线_发布订阅模式

C++中实现事件总线,主要思路是维护一个事件类型到订阅者列表的映射。当某个事件发生时,事件总线负责通知所有订阅了该事件的订阅者。以下是一种基础实现方式,可以根据具体需求进行扩展:

#include 
#include 
#include 
#include 
#include 

// 定义事件基类
class Event {
public:
    virtual ~Event() = default;
};

// 定义事件处理函数的类型别名
using EventHandler = std::function;

// 事件总线类
class EventBus {
public:
    // 订阅事件
    void subscribe(const std::string& eventType, EventHandler handler) {
        subscribers_[eventType].push_back(handler);
    }

    // 取消订阅事件
    void unsubscribe(const std::string& eventType, EventHandler handler) {
        auto& subs = subscribers_[eventType];
        subs.erase(std::remove_if(subs.begin(), subs.end(),
                                  [&](const EventHandler& h) { return h.target() == handler.target(); }),
                   subs.end());
    }

    // 发布事件
    void publish(Event* event) {
        const std::string eventType = typeid(*event).name(); // 获取事件类型名称
        auto it = subscribers_.find(eventType);
        if (it != subscribers_.end()) {
            for (const auto& handler : it->second) {
                handler(event);
            }
        }
        delete event; // 重要:事件由发布者创建,总线负责传递,最后由总线释放内存
    }

private:
    std::map> subscribers_;
};

// 示例事件类
class ButtonClickEvent : public Event {
public:
    ButtonClickEvent(int buttonId) : buttonId_(buttonId) {}
    int buttonId() const { return buttonId_; }
private:
    int buttonId_;
};

// 示例订阅者类
class Logger {
public:
    void onButtonClick(Event* event) {
        ButtonClickEvent* clickEvent = dynamic_cast(event);
        if (clickEvent) {
            std::cout << "Button " << clickEvent->buttonId() << " clicked!" << std::endl;
        }
    }
};

int main() {
    EventBus bus;
    Logger logger;

    // 订阅事件
    bus.subscribe(typeid(ButtonClickEvent).name(), std::bind(&Logger::onButtonClick, &logger, std::placeholders::_1));

    // 发布事件
    bus.publish(new ButtonClickEvent(1));
    bus.publish(new ButtonClickEvent(2));

    return 0;
}

这个例子展示了如何定义事件、订阅事件、发布事件。关键点在于使用typeid来获取事件的类型信息,这使得我们可以根据事件类型来分发事件。 注意,事件的内存管理是个需要考虑的问题,这里选择在publish函数中释放事件内存。

C++中如何设计事件总线_发布订阅模式

如何避免事件总线成为性能瓶颈?

事件总线在大型系统中可能会成为性能瓶颈。想象一下,如果一个事件被成千上万的订阅者订阅,每次发布事件都需要通知所有订阅者,这会消耗大量的CPU和网络资源。可以考虑以下优化策略:

  • 异步处理: 使用消息队列(例如RabbitMQ、Kafka)来实现事件总线。发布者将事件发送到消息队列,订阅者从消息队列中异步拉取事件。这样可以解耦发布者和订阅者,提高系统的吞吐量。
  • 事件过滤: 允许订阅者指定更精确的过滤条件。例如,可以基于事件的内容进行过滤,只接收满足特定条件的事件。这可以减少订阅者需要处理的事件数量。
  • 分层事件总线: 将事件总线分成多个层级。例如,可以有一个全局事件总线,用于处理全局性的事件;同时,可以有多个局部事件总线,用于处理特定模块的事件。这样可以减少单个事件总线的负载。
  • 线程池: 使用线程池来并发地处理事件。这样可以提高事件总线的并发处理能力。

如何处理事件处理中的异常?

事件处理函数可能会抛出异常。如果事件总线直接调用事件处理函数,那么异常可能会导致程序崩溃。因此,需要一种机制来处理事件处理中的异常。

  • try-catch块: 在事件总线内部使用try-catch块来捕获事件处理函数抛出的异常。捕获到异常后,可以记录日志、发送告警,或者采取其他适当的措施。
  • 错误处理函数: 允许订阅者注册一个错误处理函数。当事件处理函数抛出异常时,事件总线会调用该错误处理函数。错误处理函数可以负责记录日志、发送告警,或者采取其他适当的措施。
  • 返回值: 要求事件处理函数返回一个状态码,表示事件处理是否成功。如果事件处理失败,事件总线可以记录日志、发送告警,或者采取其他适当的措施。

选择哪种方法取决于具体的需求。 使用try-catch块是最简单的方法,但它可能会隐藏一些错误。 使用错误处理函数可以提供更灵活的错误处理机制。 使用返回值可以提供更明确的错误信息。

如何保证事件的顺序性?

在某些场景下,事件的顺序非常重要。例如,如果一个事件表示账户余额增加,另一个事件表示账户余额减少,那么这两个事件的顺序必须得到保证。否则,可能会导致账户余额错误。

  • 单线程处理: 使用单线程来处理事件。这样可以保证事件按照发布的顺序被处理。但是,单线程处理可能会成为性能瓶颈。
  • 序列化: 将事件序列化到消息队列中,然后使用单线程的消费者来消费消息。这样可以保证事件按照发布的顺序被处理,同时可以利用消息队列的持久化能力。
  • 版本号: 为每个事件分配一个版本号。订阅者可以根据版本号来判断事件的顺序。如果订阅者收到的事件的版本号小于它已经处理过的事件的版本号,那么订阅者可以忽略该事件。

选择哪种方法取决于具体的需求。 单线程处理是最简单的方法,但它可能会成为性能瓶颈。 序列化可以保证事件的顺序,但它会增加系统的复杂性。 版本号可以提供更灵活的顺序保证机制。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
作者最新文章
编程开发 C++
相关文章 更多
C++动态数组初始化怎么写?常用语句与代码示例
C++动态数组初始化怎么写?常用语句与代码示例

深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

C++类构造与析构函数详解
C++类构造与析构函数详解

C++类构造与析构函数详解 C++这门语言,可以说是从C语言这棵大树上衍生出的高级果实,如今的应用普及度有目共睹。作为一种静态类型的通用编程语言,它厉害的地方在于融合了多种编程哲学——无论是传统的面向过程,还是主流的面向对象,乃至数据抽象、泛型编程这些高级概念,它都能很好地支持。正因为这份卓越的扩展

C++中std::upper
C++中std::upper

C++中std::upper_bound用法解析 在C++标准模板库(STL)的算法工具箱里,upper_bound() 绝对算得上是一把精准的“探针”。它的核心任务很明确:在一个已经排好序的区间 [first, last) 内,帮你快速定位到第一个**严格大于**指定值 value 的那个元素。这

C++常对象与成员解析
C++常对象与成员解析

C++中“常”概念全景解析:从对象、成员到指针与引用 在C++的世界里,“常量性”是一个强大的保障机制。它不仅仅是一个const关键字那么简单,而是构建健壮、安全程序的重要基石。今天,我们就来系统梳理一下围绕“常”的一系列概念:常成员、常对象、常指针与常引用。理解它们,是写出高质量C++代码的关键一

c++如何将结构体写入SQLite数据库_对象关系映射方案【进阶】
c++如何将结构体写入SQLite数据库_对象关系映射方案【进阶】

直接用SQLiteCAPI绑定结构体需要手动处理字段展平与顺序对齐,ORM库虽省代码但存在隐式行为风险。小项目建议原生绑定,中大型项目可搭配编译期校验使用轻量ORM。绑定std::string需传指针与长度,注意生命周期;浮点字段比较需设误差范围。

什么是编程,学习编程的意义是?
什么是编程,学习编程的意义是?

编程是编写程序指令,让设备按意图运行,常用Python、C等语言或Scratch图形化编程。学习编程能跟上科技时代,锻炼自学与解决问题能力,提升逻辑思维,代表行动力,推动事物前进。各司其职,编程者尽本分。

编程是门艺术_是编程艺术吗?
编程是门艺术_是编程艺术吗?

编程被争论是否为艺术。从定义看,编程是技能表达,但并非所有编程都是应用开发。写代码如音乐、文章,有独特风格。好代码需耐心与逻辑能力。编程是艺术,但应用开发不等同于编程艺术。

【编程的感受】
【编程的感受】

很多人提起编程,总觉得这是一门“不起眼”的学问——不就是写写代码嘛,能有多深?但真走进去了才会发现,编程里头的门道,远比表面看上去要丰富得多。 学习编程的过程中,最容易卡住的往往是那些基础语法。就拿那个常见的 for 循环来说吧,刚开始时怎么都绕不过弯来:为什么这么写能循环?循环条件到底怎么控制?一

对于懂得编程的人来说,编程对你来说有什么乐趣?编程大概是什么感觉?
对于懂得编程的人来说,编程对你来说有什么乐趣?编程大概是什么感觉?

解决编程问题后的痛快、完成作品的成就感、获取知识的满足感,以及用代码实现简化生活的惊喜与踏实,这些是编程独有的乐趣,只有真正写代码的人才能体会其中的酸甜苦辣、惊喜与满足,令人回味无穷。

趣味编程:少儿编程思维的启蒙之旅
趣味编程:少儿编程思维的启蒙之旅

Scratch图形化编程以拖拽积木块方式启蒙少儿编程思维,无需记忆代码语法。通过素材库、即时反馈和社区协作,帮助孩子在创作中理解顺序执行、条件判断等核心概念,锻炼逻辑、创造力和问题解决能力。

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

PDF教程
PDF教程

正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

Mac软件 更多
Adobe Acrobat Pro macOS版
Adobe Acrobat Pro macOS版
Mac

Adobe Acrobat Pro 是面向办公、法务、财务、教育及出版用户的专业PDF处理软件,可在Mac上创建、编辑、转换、合并和保护文档,并提供OCR文字识别、表单、批注、内容比较、敏感信息遮盖及电子签名功能。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

Shapr3D macOS版
Shapr3D macOS版
Mac

Shapr3D是一款面向工业设计、机械工程、建筑概念和三维打印工作流的CAD软件。Mac版采用Parasolid建模内核,支持草图约束、实体建模、工程图、可视化渲染及常见CAD格式交换,并可通过账户在多台设备之间同步项目。

WINDOWS 更多
AutoCAD Windows版
AutoCAD Windows版
Windows

AutoCAD 是 Autodesk 推出的专业计算机辅助设计软件,Windows 版支持精确二维绘图、三维实体与曲面建模、图纸标注、布局发布和任务自动化,并包含多套行业专业化工具组合,适用于建筑、机械、电气、工程建设及制造设计人员。

Adobe Acrobat Pro Windows版
Adobe Acrobat Pro Windows版
Windows

Adobe Acrobat Pro 是面向专业 PDF 工作的综合解决方案,可在 Windows 中创建、编辑、转换、比较、整理和保护文档,并支持表单、电子签名、扫描件文字识别及敏感信息永久遮盖。订阅还包含网页、移动服务和云端协作能力,

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。