不能。裸用 std::queue + std::mutex 仅保证读写互斥,缺乏等待/通知机制,消费者需忙等或 sleep,易丢消息、浪费 CPU;必须组合 std::condition_variable 实现空时挂起、有数据时唤醒,并用 while 循环检查条件防虚假唤醒。

std::queue + std::mutex 能不能直接用于跨线程 pub/sub?
不能。裸用 std::queue 本身不是线程安全的,即使加了 std::mutex,也只解决了“读写互斥”问题,没解决“等待通知”——消费者线程得轮询或 sleep,浪费 CPU,还可能丢消息。
真正可用的最小可行方案是组合:std::queue + std::mutex + std::condition_variable。其中 std::condition_variable 负责让空队列时消费者挂起、有数据时被唤醒。
- 发布者调用
notify_one()或notify_all()告诉至少一个/所有等待中的消费者“有新消息” - 消费者必须在
wait()前已持锁,并传入一个谓词(比如!queue.empty()),避免虚假唤醒 - 别忘了在
wait()返回后重新检查条件——这是标准做法,不是可选项
如何避免 shared_ptr 引用计数竞争导致的性能瓶颈?
当发布大量小消息(如 std::shared_ptr<Event>)时,频繁拷贝 shared_ptr 会触发原子引用计数增减,在高并发下成为热点。这不是理论问题,实测在 16 核机器上每秒百万级发布时,shared_ptr 构造/析构能占到 15%+ CPU 时间。
更轻量的做法是:用 std::unique_ptr<Event> + 移动语义。发布端 move 进队列,订阅端 move 出队列。只要确保每个消息只被一个消费者处理(即点对点或广播但各消费者独立取),就完全不需要共享所有权。
立即学习“C++免费学习笔记(深入)”;
- 队列类型应为
std::queue<std::unique_ptr<Event>>,而非std::queue<std::shared_ptr<Event>> - 入队用
queue.push(std::move(ptr)),出队用auto ptr = std::move(queue.front()); queue.pop(); - 如果真需要多消费者同时访问同一消息(罕见),再考虑
shared_ptr,但务必把拷贝动作限制在必要位置(比如只在分发给每个 subscriber 前拷贝一次)
std::function + type-erasure 怎么支持不同事件类型的统一分发?
硬编码每种事件类型(onMouseClick、onDataReady)会导致订阅接口爆炸。用 std::function<void(const EventBase&)> 统一接收,靠运行时 dynamic_cast 或 std::visit(若用 std::variant)做分发,是常见折中。
但要注意:基类 EventBase 必须有虚析构函数,否则 unique_ptr<EventBase> 释放派生对象会未定义行为;且 std::function 的类型擦除本身有约 16–32 字节开销和间接调用成本。
- 若事件种类少且稳定,优先用
enum class EventType+std::variant<MouseEvent, DataEvent, ...>,避免虚函数表查找 - 订阅时传入的 lambda 捕获要谨慎:避免捕获 this 指针而延长对象生命周期,引发循环引用或 use-after-free
- 发布时不要在锁内调用
std::function回调——否则可能造成锁持有时间不可控,甚至死锁(比如回调里又去 publish)
为什么 std::jthread + stop_token 比 std::thread 更适合管理订阅者生命周期?
传统 std::thread 没有内置的协作式中断机制,停止消费者线程只能靠设置标志位 + 条件变量超时等待,容易错过最后一条消息或卡住。
std::jthread 自带 std::stop_token,配合 wait_until 和 stop_requested(),能让消费者线程在收到停止请求后,自然处理完当前队列剩余消息再退出。
- 构造
std::jthread时传入的函数,第一个参数自动是std::stop_token - 消费者循环中用
cv.wait_until(lock, timeout, [&]{ return stop_token.stop_requested() || !queue.empty(); }) - 发布者调用
jthread.request_stop()后,消费者会在下次 wait 唤醒时检查并退出,不会丢消息
这个细节很多人忽略:不靠 std::jthread 的 stop 机制,光靠 mutex + cv 很难写出既安全又不丢消息的优雅退出逻辑。


















