Qt插件架构消息应该支持同步消息吗?
一、结论
当前的插件系统 MessageBus:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
| 同步消息(请求 / 响应) |
|
为什么不支持同步消息,原因不是“做不到”,而是:
同步消息 ≠ publish/subscribe同步本质是 RPC / Call-Return,而不是事件。
二、为什么不能直接“同步 publish”
如果这样想:
bus->publish(msg);auto result = msg.payload["result"]; // ❌
. 这里会有 3 个根本问题
. 1️⃣ 发布者不知道谁会处理
- 多个订阅者?哪个返回?
- 没人订阅?卡死?
. 2️⃣ 跨线程
- Qt 事件队列是异步的
- 同步 = 阻塞线程 = 很容易死锁 UI
. 3️⃣ 插件生命周期
- 目标插件可能刚好卸载
所以:同步消息必须是“显式设计”,不能靠 publish 偷偷实现。
三、工程上“正确”的三种同步方案
✅ 方案 1:Request / Response(最推荐)
异步实现 + 同步语义
. 3.1 消息扩展
structMessageHeader { MessageType type; quint32 senderId; quint64 timestamp; QUuid requestId; // 新增bool isResponse{false};};
. 3.2 请求方(同步封装)
Message request;request.header.type = MessageType::QueryConfig;request.header.requestId = QUuid::createUuid();QEventLoop loop;Message response;bus->subscribe( MessageType::QueryConfig, &loop, SLOT(quit()));bus->publish(request);// 阻塞等待loop.exec();// response 由订阅方 publish 回来
👉 核心思想:
- 底层仍然是异步
-
上层用 QEventLoop / QSemaphore包装成同步
. 3.3 响应方
void PluginB::onQueryConfig(const Message& req){ Message resp; resp.header.type = MessageType::QueryConfig; resp.header.requestId = req.header.requestId; resp.header.isResponse = true; resp.payload["value"] = 42; bus->publish(resp);}
. 🟢 优点
- 不破坏 MessageBus 设计
- 跨线程安全
- 可超时
- 工程可控
. 🔴 缺点
- 写法稍复杂
⚠️ 方案 2:单接收者同步调用(RPC 风格)
只适合 明确 1 对 1 的服务型插件
. 3.4 接口扩展
classIMessageBus {public:virtualboolcall( MessageType type,const Message& request, Message& response,int timeoutMs = 1000 )= 0;};
. 3.5 实现思路
- MessageBus 内部维护:
QHash<MessageType, Subscriber> m_rpcHandlers;
-
使用:
-
同线程:
Qt::DirectConnection -
跨线程:
BlockingQueuedConnection
QMetaObject::invokeMethod( handler.receiver, handler.slot.constData(), Qt::BlockingQueuedConnection, Q_RETURN_ARG(Message, response), Q_ARG(Message, request));
. 🟢 优点
- 像函数调用
- 使用简单
. 🔴 致命缺点
- BlockingQueuedConnection 很容易死锁
- 插件卸载风险极高
- 破坏“广播模型”
⚠️ 方案 3:Future / Promise(高级用法)
QFuture<Message> future = bus->request(msg);future.waitForFinished();auto resp = future.result();
底层:
QPromiseQSharedPointer
🟢 适合:
- 计算型插件
- 无 UI 线程阻塞
🔴 复杂度最高
四、“架构级建议”
事件总线 ≠ RPC 框架
. 正确拆分方式
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
五、如果现在讨论:该不该支持“同步消息”?
答案是非常明确的:
❌ 不要在 MessageBus 里直接做同步 publish✅ 用 Request/Response 模式,在“使用层”提供同步包装
夜雨聆风