背景
由于业务之间的解耦,通常我们在一个进程中会设计多个模块,模块之间的通信有时不需要消息队列,但是需要一个通信的机制。
示例1------消息内容使用基类
cpp
#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <map>
#include <mutex>
#include <memory>
// 通用消息基类(可选,用于统一传递数据)
struct Message {
virtual ~Message() = default;
};
class MessageBus {
public:
using Callback = std::function<void(const std::shared_ptr<Message>&)>;
// 获取单例
static MessageBus& instance() {
static MessageBus bus;
return bus;
}
// 订阅:指定字符串主题
void subscribe(const std::string& topic, Callback callback) {
std::lock_guard<std::mutex> lock(mutex_);
subscribers_[topic].push_back(std::move(callback));
}
// 发布:指定字符串主题和消息内容
void publish(const std::string& topic, const std::shared_ptr<Message>& msg) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = subscribers_.find(topic);
if (it == subscribers_.end()) return;
// 复制回调列表,避免执行过程中修改容器
auto callbacks = it->second;
// 释放锁后执行,防止死锁
lock.unlock();
for (const auto& cb : callbacks) {
try {
cb(msg);
} catch (...) {
std::cerr << "Error in subscriber for topic: " << topic << std::endl;
}
}
}
private:
MessageBus() {}
std::mutex mutex_;
// Key: 主题名称 (String), Value: 回调列表
std::map<std::string, std::vector<Callback>> subscribers_;
};
// ================= 测试示例 =================
struct OrderMsg : public Message {
int orderId;
OrderMsg(int id) : orderId(id) {}
};
struct LogMsg : public Message {
std::string content;
LogMsg(const std::string& c) : content(c) {}
};
int main() {
auto& bus = MessageBus::instance();
// 1. 订阅 "order" 主题
bus.subscribe("order", [](const std::shared_ptr<Message>& msg) {
auto order = std::dynamic_pointer_cast<OrderMsg>(msg);
if (order) {
std::cout << "[OrderModule] 收到订单: " << order->orderId << std::endl;
}
});
// 2. 订阅 "log" 主题
bus.subscribe("log", [](const std::shared_ptr<Message>& msg) {
auto log = std::dynamic_pointer_cast<LogMsg>(msg);
if (log) {
std::cout << "[LogModule] 收到日志: " << log->content << std::endl;
}
});
// 3. 发布消息
bus.publish("order", std::make_shared<OrderMsg>(1001));
bus.publish("log", std::make_shared<LogMsg>("System Start"));
return 0;
}
示例2------消息类型模板化
cpp
#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <map>
#include <mutex>
#include <memory>
#include <typeindex>
// ================= 1. 内部存储辅助类 =================
// 用于在 Map 中存储不同类型的回调,并管理数据的生命周期
class MessageBus {
public:
static MessageBus& instance() {
static MessageBus bus;
return bus;
}
// 订阅:指定主题和回调
template <typename T>
void subscribe(const std::string& topic, std::function<void(const T&)> callback) {
std::lock_guard<std::mutex> lock(mutex_);
// 使用 type_index 确保同一主题下不同类型的回调隔离(可选,若约定主题唯一类型可省略)
auto key = std::make_pair(topic, std::type_index(typeid(T)));
// 包装回调:从 void* 恢复为 T&
auto wrapper = [callback](void* data) {
callback(*static_cast<const T*>(data));
};
registry_[key].push_back(wrapper);
}
// 发布:值传递,内部会拷贝一份数据
template <typename T>
void publish(const std::string& topic, const T& message) {
std::lock_guard<std::mutex> lock(mutex_);
auto key = std::make_pair(topic, std::type_index(typeid(T)));
auto it = registry_.find(key);
if (it == registry_.end()) return;
// 【关键】:拷贝消息数据,确保生命周期独立于发布者
// 使用 shared_ptr 管理拷贝后的数据,确保所有回调执行期间数据有效
auto dataCopy = std::make_shared<T>(message);
auto callbacks = it->second;
lock.unlock();
for (const auto& cb : callbacks) {
try {
// 传递拷贝数据的指针,回调中转换为引用
cb(dataCopy.get());
} catch (...) {
std::cerr << "Error in subscriber." << std::endl;
}
}
}
private:
MessageBus() {}
std::mutex mutex_;
// Key: <主题名, 类型信息>, Value: 类型擦除的回调列表
using Key = std::pair<std::string, std::type_index>;
std::map<Key, std::vector<std::function<void(void*)>>> registry_;
};
// ================= 2. 测试示例 =================
struct OrderEvent {
int orderId;
std::string customer;
OrderEvent(int id, const std::string& name) : orderId(id), customer(name) {
std::cout << "OrderEvent Created: " << orderId << std::endl;
}
~OrderEvent() {
std::cout << "OrderEvent Destroyed: " << orderId << std::endl;
}
};
int main() {
auto& bus = MessageBus::instance();
// 订阅
bus.subscribe<OrderEvent>("order", [](const OrderEvent& e) {
std::cout << "[Subscriber] Received Order: " << e.orderId << ", " << e.customer << std::endl;
});
{
// 发布:传入局部变量(值传递)
OrderEvent localOrder(101, "Bob");
std::cout << "--- Publishing ---" << std::endl;
bus.publish("order", localOrder);
std::cout << "--- Publish Call Finished ---" << std::endl;
}
// localOrder 在此处销毁,但订阅者收到的拷贝依然有效
return 0;
}