首页 5G技术

C++ 设计模式深度解析:观察者模式实战与避坑指南(下)

分类:5G技术
字数: (8156)
阅读: (3791)
内容摘要:C++ 设计模式深度解析:观察者模式实战与避坑指南(下),

上一篇文章中,我们已经了解了观察者模式的基本概念和同步通知的实现。在实际的 C++ 项目中,尤其是在高并发场景下,同步通知可能会阻塞主线程,导致响应缓慢。因此,异步通知和线程安全是观察者模式更高级的应用。本篇我们深入探讨 观察者模式 在异步场景下的实现,以及如何保证线程安全。

异步通知的实现

异步通知的核心思想是将通知过程放到独立的线程中执行,避免阻塞主线程。可以使用 C++11 的 std::thread 或者线程池来实现。

C++ 设计模式深度解析:观察者模式实战与避坑指南(下)

使用 std::thread 的简单示例:

C++ 设计模式深度解析:观察者模式实战与避坑指南(下)
#include <iostream>
#include <vector>
#include <thread>
#include <functional>

class Subject {
public:
  void attach(std::function<void()> observer) {
    observers_.push_back(observer);
  }

  void detach(std::function<void()> observer) {
    // ... 实现移除 observer 的逻辑
  }

  void notify() {
    for (auto& observer : observers_) {
      std::thread t(observer); // 创建新线程执行 observer
      t.detach(); // 让线程独立运行
    }
  }

private:
  std::vector<std::function<void()>> observers_;
};

class ConcreteObserver {
public:
  void update() {
    std::cout << "Observer notified in thread: " << std::this_thread::get_id() << std::endl;
  }
};

int main() {
  Subject subject;
  ConcreteObserver observer1;
  ConcreteObserver observer2;

  subject.attach([&]() { observer1.update(); });
  subject.attach([&]() { observer2.update(); });

  subject.notify();

  // 主线程可能比子线程先结束,需要处理好同步问题
  std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 简单等待

  return 0;
}

注意事项:

C++ 设计模式深度解析:观察者模式实战与避坑指南(下)
  • 使用 std::thread 需要注意线程的生命周期管理。t.detach() 会让线程独立运行,但主线程可能在子线程完成之前就结束了。可以使用 t.join() 等待线程结束,但会阻塞主线程。通常,更好的方式是使用线程池,统一管理线程的生命周期。
  • 上述代码仅仅是演示异步通知的基本实现,没有处理异常情况。实际项目中需要增加异常处理机制。

线程安全问题与解决方案

在多线程环境下,多个线程可能同时访问和修改观察者列表,导致数据竞争。需要使用锁机制来保护共享资源。

C++ 设计模式深度解析:观察者模式实战与避坑指南(下)

使用 std::mutex 保护观察者列表:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <functional>

class Subject {
public:
  void attach(std::function<void()> observer) {
    std::lock_guard<std::mutex> lock(mutex_);
    observers_.push_back(observer);
  }

  void detach(std::function<void()> observer) {
    std::lock_guard<std::mutex> lock(mutex_);
    // ... 实现移除 observer 的逻辑,同样需要加锁
  }

  void notify() {
    std::lock_guard<std::mutex> lock(mutex_); //保护observers_的读取
    for (auto& observer : observers_) {
      std::thread t(observer); // 创建新线程执行 observer
      t.detach(); // 让线程独立运行
    }
  }

private:
  std::vector<std::function<void()>> observers_;
  std::mutex mutex_;
};

注意事项:

  • 使用 std::lock_guard 可以自动管理锁的生命周期,避免忘记解锁导致死锁。
  • 所有访问和修改 observers_ 的地方都需要加锁,包括 attachdetachnotify 方法。
  • 如果观察者的 update 方法也需要访问共享资源,同样需要加锁。需要仔细设计锁的粒度,避免过度加锁导致性能下降。
  • 在高并发情况下,可以考虑使用读写锁(std::shared_mutex)来提高性能。读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。

实战避坑经验总结

  1. 避免循环依赖: 观察者和主题之间可能存在循环依赖,导致内存泄漏或者无限循环。需要仔细设计类之间的关系,避免出现这种情况。
  2. 处理异常: 在异步通知中,如果观察者的 update 方法抛出异常,可能会导致程序崩溃。需要在 notify 方法中捕获异常,并进行适当的处理,例如记录日志或者通知错误处理模块。
  3. 性能优化: 当观察者数量很多时,notify 方法的性能可能会成为瓶颈。可以考虑使用更高效的数据结构来存储观察者,例如哈希表。也可以使用线程池来限制并发线程的数量,避免系统资源耗尽。
  4. 序列化问题: 当观察者或者主题需要序列化时,需要注意处理观察者列表。通常,不应该序列化观察者列表,而是应该在反序列化之后重新注册观察者。
  5. Nginx 反向代理中监听端口变化: 在 Nginx 反向代理配置中,如果后端服务监听的端口发生变化,需要及时更新 Nginx 的配置。可以使用观察者模式,让 Nginx 配置管理模块监听后端服务的端口变化事件,并在端口变化时自动更新 Nginx 配置并重新加载。这可以结合宝塔面板的 API 接口实现自动化。
  6. 负载均衡中的服务器健康检查: 在负载均衡系统中,需要定期检查后端服务器的健康状态。可以使用观察者模式,让负载均衡器监听后端服务器的健康状态变化事件,并在服务器状态变化时自动调整流量分配策略。这涉及到对后端服务器并发连接数的监控,以及服务熔断机制的设计。

掌握了异步通知和线程安全,以及避坑经验,才能在实际项目中更好地应用 观察者模式

C++ 设计模式深度解析:观察者模式实战与避坑指南(下)

转载请注明出处: 代码一只喵

本文的链接地址: http://m.acea2.store/blog/940720.SHTML

本文最后 发布于2026-04-17 15:34:09,已经过了10天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 打工人日记 2 天前
    循环依赖这个问题确实需要注意,之前就遇到过,排查了好久才发现是观察者模式导致的。
  • 山西刀削面 2 天前
    感谢分享,C++ 的多线程确实是个坑,锁的粒度控制很重要,学到了。
  • e人代表 4 天前
    感谢分享,C++ 的多线程确实是个坑,锁的粒度控制很重要,学到了。
  • 秋名山车神 1 天前
    文章提到了 Nginx 的反向代理和负载均衡,很有实用价值,希望能多一些这方面的实战案例。