std::future 不适用于观察者模式:仅支持单次 get()、无法注册多个回调、不支持值变更即触发。其本质是单次消费的一次性结果容器,与观察者所需的多次通知、多监听者、非阻塞响应天然冲突。

std::future 不能直接用于观察者模式的三个硬伤
想用 std::future 当作观察者模式的通知载体?会卡在三个地方:它只能被 get() 一次、无法注册多个回调、不支持“值变更即触发”而是“完成即阻塞”。std::future 本质是单次消费的一次性结果容器,和观察者需要的“多次通知 + 多监听者 + 非阻塞响应”天然冲突。
常见错误现象:std::future::wait_for 轮询导致 CPU 空转;std::future::get() 在第二次调用时抛出 std::future_error: No associated state;试图用 std::shared_future 绕过单次限制,却发现仍无法注册回调函数。
- 使用场景错配:适合“发请求→等结果”,不适合“数据源变化→推送给所有订阅者”
- 参数差异:
std::promise只能set_value()一次,而观察者模式中状态可能更新数十次 - 性能影响:强行轮询
wait_for(1ms)会显著拉高线程调度开销
用 std::shared_ptr + std::function 实现轻量级可订阅信号
真正可行的路径是绕过 std::future,改用共享状态 + 回调注册。核心结构是一个持有当前值的 std::shared_ptr<T> 和一个 std::vector<std::function<void(const T&)>> 存储监听器。
关键设计点在于“通知不阻塞”和“监听器可动态增删”:每次 notify() 时遍历回调列表,用 const T& 传参避免拷贝;监听器注册返回一个 std::function<void()> 的取消句柄,内部通过 std::weak_ptr 弱引用管理生命周期,防止循环引用。
立即学习“C++免费学习笔记(深入)”;
class Signal {
std::shared_ptr<int> value_;
mutable std::mutex mtx_;
std::vector<std::function<void(const int&)>> observers_;
public:
void set(int v) {
std::lock_guard<std::mutex> lk(mtx_);
*value_ = v;
for (auto& obs : observers_) obs(v);
}
auto subscribe(std::function<void(const int&)> f) -> std::function<void()> {
std::lock_guard<std::mutex> lk(mtx_);
observers_.push_back(f);
return [this, f]() mutable {
std::lock_guard<std::mutex> lk(mtx_);
observers_.erase(
std::remove(observers_.begin(), observers_.end(), f),
observers_.end()
);
};
}
};
如何把 std::future 的完成事件桥接到观察者通知
如果你确实有一组异步任务(比如多个 std::async),想让它们的完成触发观察者,不要把 std::future 暴露给业务层,而应在封装层将其“翻译”成信号事件。
做法是:每个 std::future 启动后,在独立线程中调用 wait() + get(),然后调用 Signal::set() 或专用的 Signal::notify_on_complete()。这样既复用了 std::future 的异步能力,又不破坏观察者语义。
- 避免在主线程阻塞等待:
std::future::wait()必须在非 UI/主逻辑线程里执行 - 异常需显式处理:若
std::future::get()抛异常,必须捕获并转为错误通知(如Signal<std::optional<int>>或单独的ErrorSignal) - 性能提示:大量并发
std::future时,建议用线程池统一调度等待逻辑,而非为每个 future 创建新线程
std::atomic + observer pattern 的无锁尝试为何容易翻车
有人试图用 std::atomic<int> 替代共享指针来优化性能,但很快发现:原子变量只保证读写安全,不提供“变更通知”机制。你仍然得配合轮询或条件变量,而一旦引入 std::condition_variable,就回到了传统同步模型,失去了响应式“数据驱动”的简洁性。
更隐蔽的问题是内存序:std::atomic_thread_fence 或 memory_order_seq_cst 在多监听器场景下难以精确控制回调执行时机,容易出现通知丢失或重复——尤其是当某个观察者回调内发生耗时操作时,后续变更可能被覆盖。
结论很实际:除非你在嵌入式或超低延迟场景且监听器数量恒为 1,否则别碰无锁观察者。标准互斥+共享状态的组合,在绝大多数 C++ 服务端或桌面应用中,性能足够且行为可预测。
最常被忽略的点是监听器执行上下文——所有回调默认在通知线程中运行。如果某次 set() 来自网络 IO 线程,而某个回调里做了文件写入,整个网络线程就会被拖慢。真正的响应式系统必须支持回调分发到指定 executor,这已超出 std::future 和裸 std::thread 的能力范围。



















