乐于分享
好东西不私藏

Qt插件架构消息应该支持同步消息吗?

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();

底层:

  • QPromise
  • QSharedPointer

🟢 适合:

  • 计算型插件
  • 无 UI 线程阻塞

🔴 复杂度最高


四、“架构级建议”

事件总线 ≠ RPC 框架

正确拆分方式

场景
用什么
状态变化 / 通知
MessageBus(异步)
查询 / 获取数据
Request / Response
强依赖服务
明确接口(Service Plugin)

五、如果现在讨论:该不该支持“同步消息”?

答案是非常明确的:

❌ 不要在 MessageBus 里直接做同步 publish✅ 用 Request/Response 模式,在“使用层”提供同步包装