0

0

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

冰火之心

冰火之心

发布时间:2025-07-25 08:15:02

|

897人浏览过

|

来源于php中文网

原创

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

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

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

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函数中释放事件内存。

ChatX翻译
ChatX翻译

最实用、可靠的社交类实时翻译工具。 支持全球主流的20+款社交软件的聊天应用,全球200+语言随意切换。 让您彻底告别复制粘贴的翻译模式,与世界各地高效连接!

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

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

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

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

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

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

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

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

如何保证事件的顺序性?

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

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

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

相关专题

更多
rabbitmq和kafka有什么区别
rabbitmq和kafka有什么区别

rabbitmq和kafka的区别:1、语言与平台;2、消息传递模型;3、可靠性;4、性能与吞吐量;5、集群与负载均衡;6、消费模型;7、用途与场景;8、社区与生态系统;9、监控与管理;10、其他特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

199

2024.02.23

kafka消费者组有什么作用
kafka消费者组有什么作用

kafka消费者组的作用:1、负载均衡;2、容错性;3、广播模式;4、灵活性;5、自动故障转移和领导者选举;6、动态扩展性;7、顺序保证;8、数据压缩;9、事务性支持。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

166

2024.01.12

kafka消费组的作用是什么
kafka消费组的作用是什么

kafka消费组的作用:1、负载均衡;2、容错性;3、灵活性;4、高可用性;5、扩展性;6、顺序保证;7、数据压缩;8、事务性支持。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

149

2024.02.23

rabbitmq和kafka有什么区别
rabbitmq和kafka有什么区别

rabbitmq和kafka的区别:1、语言与平台;2、消息传递模型;3、可靠性;4、性能与吞吐量;5、集群与负载均衡;6、消费模型;7、用途与场景;8、社区与生态系统;9、监控与管理;10、其他特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

199

2024.02.23

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

472

2023.08.10

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

65

2025.12.31

php网站源码教程大全
php网站源码教程大全

本专题整合了php网站源码相关教程,阅读专题下面的文章了解更多详细内容。

45

2025.12.31

视频文件格式
视频文件格式

本专题整合了视频文件格式相关内容,阅读专题下面的文章了解更多详细内容。

40

2025.12.31

不受国内限制的浏览器大全
不受国内限制的浏览器大全

想找真正自由、无限制的上网体验?本合集精选2025年最开放、隐私强、访问无阻的浏览器App,涵盖Tor、Brave、Via、X浏览器、Mullvad等高自由度工具。支持自定义搜索引擎、广告拦截、隐身模式及全球网站无障碍访问,部分更具备防追踪、去谷歌化、双内核切换等高级功能。无论日常浏览、隐私保护还是突破地域限制,总有一款适合你!

41

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 5.8万人学习

C 教程
C 教程

共75课时 | 3.8万人学习

C++教程
C++教程

共115课时 | 10.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号