c++ - 通过多个线程自行取消/注册观察者

标签 c++ multithreading observers

我在正确锁定通知中心时遇到问题 与 self 注册的观察员。具体来说,我在事件调度中遇到了已经被销毁的对象。

考虑以下结构:

  • 通知中心正在向多个观察者广播事件
  • 观察者在创建/删除时注册/注销自己。
    • 这随时可能发生
  • 事件派发可能与观察者注册在不同的线程上。
  • 为了同步观察者的注册和派发,这两个功能都被锁定。

最后它应该像这个最小的例子:

#include <future>
#include <functional>
#include <chrono>
#include <thread>
#include <iostream>
#include <mutex>
#include <map>

class Event {};
class Observer;
class Center {
public:
  void addObserver(Observer &o);
  void removeObserver(Observer &o);

  void sendEvent(Event*);
  static Center& instance() {static Center center; return center;}
private:
  std::map<Observer*, std::function<void(Event*)>> m_observers;
  std::mutex m_mutex;
};
class Observer {
public:
  virtual void onEvent(Event*) = 0;
  void call(Event * event) {onEvent(event);}
  Observer() {Center::instance().addObserver(*this);}
  virtual ~Observer() {Center::instance().removeObserver(*this);}
};
void Center::addObserver(Observer &o) {
  std::lock_guard<std::mutex> l(m_mutex);
  m_observers[&o] = std::bind(&Observer::call,&o,std::placeholders::_1);
}
void Center::removeObserver(Observer &o) {
  std::lock_guard<std::mutex> l(m_mutex);
  m_observers.erase(&o);
}
void Center::sendEvent(Event* e) {
  std::lock_guard<std::mutex> l(m_mutex);
  for(auto i : m_observers) 
    i.second(e);
}
class testObs : public Observer {
  virtual void onEvent(Event*) {std::cout << "do work" << std::endl;}
};

int main()
{
  auto handle = std::async(std::launch::async, []() {
    Event e;
    while(true) {Center::instance().sendEvent(&e);}
  });
  for(;;){testObs t1,t2,t3,t4;}
  handle.get();
}

这将在一段时间后遇到运行时错误(R6025 - 纯虚函数调用)。 问题在于 Observer::~Observer() 仅在 testObs 被销毁后调用, 因此锁定为时已晚,运行事件分派(dispatch)将调用已销毁的对象。

我的问题是我是否仍然可以创建一个可靠的锁(或实现另一种彻底删除对象的方法)同时 保留大部分现有结构:

  • 自动观察员注册/注销
  • 在通知中心内没有观察员所有权

最佳答案

对于虚拟方法 onEvent,您在 Observer 的通知和构造函数之间进行竞争:

testObs还没有完全构造(虚方法初始化),但是它已经被Observer构造器注册到Center中。

要调试它,请尝试在您的构造函数中添加打印语句。

要修复它:在 testObs 的构造函数而不是 Observer 的构造函数中注册您的 testObs。

析构函数也有同样的问题:将注销移到testObs的析构函数

或者提供一个空的 onEvent 实现:

virtual void onEvent(Event*){ };

但归根结底,在对象实例完全构造之前就暴露它是一个糟糕的设计。

关于c++ - 通过多个线程自行取消/注册观察者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25667948/

相关文章:

java - 删除观察者后,LiveData 上的观察者仍然处于 Activity 状态

c++ - 调用成员函数集到特定变体的正确 C++ 变体语法是什么?

Java-单线程和多线程的压缩差异

c++ - boost::shared_mutex 多读/单写互斥

java - 循环访问共享相同父类(super class)的不同对象的列表并获取一种特定类型

ios - FirebaseobserveEventType.childAdded 无法按 promise 工作

c++ - 使用成员函数作为比较器的问题排序

c++ - 以编程方式确定是否启用异常

c++ - 全局变量减慢代码

ios - SpriteKit : performance hit while preloading SKTextureAtlas