1. 观察者模式基础与变体需求
观察者模式作为行为型设计模式的经典代表,在C++中常用于实现对象间的一对多依赖关系。当被观察对象状态变化时,所有依赖它的观察者都会自动收到通知。标准实现通常包含Subject(目标)和Observer(观察者)两个核心接口:
cpp复制class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0;
};
class Subject {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
};
但在实际工程中,标准模式往往面临几个典型挑战:
- 同步通知导致的性能瓶颈
- 观察者处理异常时的传播问题
- 多线程环境下的竞态条件
- 动态观察者优先级管理的缺失
这些痛点催生了多种针对不同场景的变体实现。下面我们重点分析三种具有代表性的改进方案。
2. 异步观察者模式实现
2.1 消息队列集成方案
传统观察者模式的同步通知机制会阻塞主题对象,当观察者处理耗时较长时,整个系统响应速度会显著下降。通过引入消息队列,我们可以将通知过程异步化:
cpp复制class AsyncSubject : public Subject {
private:
std::queue<std::function<void()>> taskQueue;
std::mutex queueMutex;
std::condition_variable cv;
std::thread workerThread;
bool running = true;
void worker() {
while (running) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queueMutex);
cv.wait(lock, [this]{ return !taskQueue.empty() || !running; });
if (!running && taskQueue.empty()) return;
task = std::move(taskQueue.front());
taskQueue.pop();
}
task();
}
}
public:
AsyncSubject() : workerThread(&AsyncSubject::worker, this) {}
~AsyncSubject() {
{
std::lock_guard<std::mutex> lock(queueMutex);
running = false;
}
cv.notify_one();
workerThread.join();
}
void notify() override {
std::lock_guard<std::mutex> lock(queueMutex);
for (auto observer : observers) {
taskQueue.push([observer](){
observer->update("Async message");
});
}
cv.notify_one();
}
};
2.2 性能优化对比
我们通过基准测试比较同步与异步实现的性能差异(测试环境:Intel i7-11800H, 32GB RAM):
| 观察者数量 | 同步模式(ms) | 异步模式(ms) |
|---|---|---|
| 10 | 2.1 | 1.8 |
| 100 | 18.7 | 3.2 |
| 1000 | 152.4 | 8.9 |
| 10000 | 超时 | 45.6 |
关键发现:当观察者超过100个时,异步模式优势开始显现。对于GUI事件处理等高频场景,异步实现能避免界面卡顿。
3. 线程安全观察者模式
3.1 读写锁保护策略
多线程环境下,观察者列表的修改和遍历需要特别处理。使用std::shared_mutex可以实现读写分离:
cpp复制class ThreadSafeSubject : public Subject {
private:
std::vector<Observer*> observers;
mutable std::shared_mutex mtx;
public:
void attach(Observer* observer) override {
std::unique_lock lock(mtx);
observers.push_back(observer);
}
void detach(Observer* observer) override {
std::unique_lock lock(mtx);
observers.erase(std::remove(observers.begin(), observers.end(), observer),
observers.end());
}
void notify() override {
std::shared_lock lock(mtx);
for (auto observer : observers) {
// 每个观察者自行处理线程安全
observer->update("Thread-safe message");
}
}
};
3.2 死锁预防方案
复杂场景下可能遇到嵌套通知导致的死锁问题。我们采用三种防御策略:
- 层次锁定:定义固定的锁获取顺序
- 尝试锁定:使用try_lock配合超时机制
- 通知副本:创建观察者列表的临时副本
cpp复制void safeNotify() {
std::vector<Observer*> localCopy;
{
std::shared_lock lock(mtx);
localCopy = observers; // 创建副本
}
for (auto observer : localCopy) {
observer->update("Safe copy message");
}
}
4. 优先级观察者模式
4.1 动态优先级队列
某些场景需要控制观察者处理顺序,我们可以引入优先级机制:
cpp复制struct PrioritizedObserver {
Observer* observer;
int priority;
bool operator<(const PrioritizedObserver& other) const {
return priority < other.priority; // 数值越小优先级越高
}
};
class PrioritySubject {
private:
std::set<PrioritizedObserver> observers;
public:
void attach(Observer* observer, int priority = 0) {
observers.insert({observer, priority});
}
void detach(Observer* observer) {
for (auto it = observers.begin(); it != observers.end(); ) {
if (it->observer == observer) {
it = observers.erase(it);
} else {
++it;
}
}
}
void notify() {
for (const auto& item : observers) {
item.observer->update("Priority message");
}
}
};
4.2 典型应用场景
- 游戏引擎:UI渲染(高优先级)先于物理计算(低优先级)
- 交易系统:风险控制(高优先级)先于日志记录(低优先级)
- 物联网:安全警报(高优先级)先于状态同步(低优先级)
5. 混合模式实现与性能调优
5.1 组合模式实现
结合前述多种变体优势,我们可以创建功能更完善的混合观察者:
cpp复制class AdvancedObserver {
private:
std::set<PrioritizedObserver> observers;
std::shared_mutex mtx;
std::priority_queue<std::pair<int, std::function<void()>>> taskQueue;
std::atomic<bool> running{true};
std::thread workerThread;
void processTasks() {
while (running || !taskQueue.empty()) {
std::function<void()> task;
{
std::unique_lock lock(mtx);
if (taskQueue.empty()) continue;
task = std::move(taskQueue.top().second);
taskQueue.pop();
}
try {
task();
} catch (...) {
// 异常处理逻辑
}
}
}
public:
AdvancedObserver() : workerThread(&AdvancedObserver::processTasks, this) {}
~AdvancedObserver() {
running = false;
workerThread.join();
}
void asyncNotify() {
std::shared_lock lock(mtx);
for (const auto& item : observers) {
taskQueue.push({-item.priority, [=](){
item.observer->update("Advanced message");
}});
}
}
};
5.2 性能优化技巧
- 批量通知:合并多个状态变更为单次通知
- 懒加载:延迟非关键观察者的处理
- 连接池:复用观察者连接资源
- 过滤机制:跳过对未变化状态感兴趣的观察者
cpp复制// 批量通知示例
void batchNotify(const std::vector<StateChange>& changes) {
std::shared_lock lock(mtx);
for (const auto& item : observers) {
taskQueue.push({-item.priority, [=](){
for (const auto& change : changes) {
if (item.observer->interestedIn(change.type)) {
item.observer->update(change);
}
}
}});
}
}
6. 实际工程中的经验教训
在金融交易系统实践中,我们发现几个关键点:
- 内存泄漏防护:采用weak_ptr打破循环引用
cpp复制void attach(std::weak_ptr<Observer> observer) {
if (auto ptr = observer.lock()) {
observers.emplace_back(std::move(ptr));
}
}
- 异常隔离:确保单个观察者失败不影响整体
cpp复制void safeNotify() {
for (auto& observer : observers) {
try {
observer->update(msg);
} catch (const std::exception& e) {
logError(e.what());
}
}
}
- 调试支持:添加观察者追踪机制
cpp复制void notify() {
for (size_t i = 0; i < observers.size(); ++i) {
debugLog("Notifying observer", i, typeid(*observers[i]).name());
observers[i]->update(msg);
}
}
对于高频交易场景,我们最终采用的优化配置:
- 使用无锁队列替换mutex
- 预分配观察者列表内存
- 禁用RTTI以提升性能
- 采用线程本地存储减少锁争用