这个事件调度员/听众会工作吗?

时间:2015-11-06 00:16:19

标签: c++ multithreading events

我正在开发一个显着多线程的程序。我需要响应对象和线程之间的事件,所以我提出了以下代码。

#ifndef EVENT_H
#define EVENT_H

#include <vector>
#include <mutex>
#include <algorithm>
#include <memory>

/**
 * Create the definition of a dispatcher for the event type (t)
 */
#define DISPATCHER(t) EventDispatcher<t>
/**
 * Create the definition of a listener for event type (t)
 */
#define LISTENER(t) DISPATCHER(t)::EventListener
/**
 * Dispatch a event (e) of type (t)
 */
#define DISPATCH(t, e) DISPATCHER(t)::dispatch(e);
/**
 * Attach a listener (l) of type (t) to the dispatcher (d)
 */
#define LISTEN(t, d, l) ((DISPATCHER(t) *)(d))->addListener((l));

template <typename T>
class EventDispatcher {
public:

  virtual ~EventDispatcher() {
    std::unique_lock<std::recursive_mutex> uLock(lock);
    // Prevent more listeners from being added.
    isAlive = false;
    // Remove all listeners.
    while (listeners.begin() != listeners.end()) {
      EventDispatcher<T>::EventListener *listener = *listeners.begin();
      // Call remove listener so that the listener will be notified of the change.
      removeListener(listener);
    }
  }

  class EventListener {
    friend EventDispatcher<T>;
  public:

    virtual ~EventListener() {
      std::unique_lock<std::recursive_mutex> uLock(lock);
      // Stop more dispatchers from connecting.
      isAlive = false;
      // Remove self from all dispatchers.
      // Use while loop as removeListener will call removeDispatcher and modify dispatchers.
      while (dispatchers.begin() != dispatchers.end()) {
        EventDispatcher<T> *dispatcher = *dispatchers.begin();
        dispatcher->removeListener(this);
      }
    }

  protected:
    /**
     * Respond to an event.
     * @param sender The dispatcher that sent the event.
     * @param event The event that occurred.
     */
    virtual void onEvent(EventDispatcher<T> *sender, std::shared_ptr<T> event) = 0;

  private:
    bool isAlive = true;
    typedef std::vector<EventDispatcher<T> *> DispatcherList;
    DispatcherList dispatchers;
    std::recursive_mutex lock;

    /**
     * Add a reference to the dispatchers this listener is attached to.
     * @param dispatcher The dispatcher that attached this listener.
     * @return true if the listener is still alive.
     */
    bool addDispatcher(EventDispatcher<T> *dispatcher) {
      if (dispatcher == NULL) {
        return false;
      }
      std::unique_lock<std::recursive_mutex> uLock(lock);
      if (isAlive) {
        if (std::find(dispatchers.begin(), dispatchers.end(), dispatcher) == dispatchers.end()) {
          // This should only ever be called by the dispatcher so no need to call addListener.
          dispatchers.push_back(dispatcher);
        }
      }
      return isAlive;
    }

    /**
     * Remove a reference to the dispatchers this listener is attached to.
     * @param dispatcher The dispatcher that removed this listener.
     */
    void removeDispatcher(EventDispatcher<T> *dispatcher) {
      if (dispatcher == NULL) {
        return;
      }
      std::unique_lock<std::recursive_mutex> uLock(lock);
      typename DispatcherList::iterator itr = std::find(dispatchers.begin(), dispatchers.end(), dispatcher);
      if (itr != dispatchers.end()) {
        // This should only ever be called by the dispatcher so no need to call removeListener.
        dispatchers.erase(itr);
      }
    }

  };

public:

  /**
   * Add a listener to the dispatcher.
   * @param listener The listener to add.
   */
  void addListener(EventDispatcher<T>::EventListener *listener) {
    if (listener == NULL) {
      return;
    }
    std::unique_lock<std::recursive_mutex> uLock(lock);
    if (isAlive) {
      if (std::find(listeners.begin(), listeners.end(), listener) == listeners.end()) {
        // Listener not in list, add it.
        if (listener->addDispatcher(this)) {
          // The listener was still alive so register it.
          listeners.push_back(listener);
        }
      }
    }
  }

  /**
   * Remove a listener from the dispatcher.
   * @param listener The listener to remove.
   */
  void removeListener(EventDispatcher<T>::EventListener *listener) {
    if (listener == NULL) {
      return;
    }
    std::unique_lock<std::recursive_mutex> uLock(lock);
    typename ListenerList::iterator itr = std::find(listeners.begin(), listeners.end(), listener);
    if (itr != listeners.end()) {
      listener->removeDispatcher(this);
      listeners.erase(itr);
    }
  }

protected:

  /**
   * Dispatch an event to all listeners.
   * @param event The event to dispatch.
   * @note If the event is modifiable then not all listeners will necessarily get the exact same message.
   * @note The event will be deleted before the function returns.
   */
  void dispatch(T *event) {
    std::shared_ptr<T> evt(event);
    std::unique_lock<std::recursive_mutex> uLock(lock);
    for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); iter++) {
      (*iter)->onEvent(this, evt);
    }
  }

  /**
   * Dispatch an event to all listeners.
   * @param event The event to dispatch.
   * @note If the event is modifiable then not all listeners will necessarily get the exact same message.
   */
  void dispatch(std::shared_ptr<T> event) {
    std::unique_lock<std::recursive_mutex> uLock(lock);
    for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); iter++) {
      (*iter)->onEvent(this, event);
    }
  }

private:
  bool isAlive = true;
  typedef std::vector<EventListener *> ListenerList;
  ListenerList listeners;
  std::recursive_mutex lock;

};

#endif  /* EVENT_H */

这是我设置的一个简单测试(不是多线程的)

#include "include/Event.h"
#include <iostream>

class ActionEvent {
public:
  ActionEvent(int id) : actionID(id) {
//    actionID = id;
  }
  const int actionID;
};

class PropertyChangeEvent {
public:
  int propertyID;
};

class testDispatcher : public DISPATCHER(ActionEvent), public DISPATCHER(PropertyChangeEvent) {
public:
  void test() {
//    dispatch(new ActionEvent(0));  // ambiguous function.
//    dispatch(new PropertyChangeEvent());  // ambiguous function.
//    EventDispatcher<ActionEvent>::dispatch(new ActionEvent(1));  // works but is long.
//    EventDispatcher<ActionEvent>::dispatch(new ActionEvent(2));  // works but is long.
//    EventDispatcher<PropertyChangeEvent>::dispatch(new PropertyChangeEvent());  // works but is long.
    DISPATCH(ActionEvent, new ActionEvent(1));  // The dispatcher will make a shared pointer then delete the event.
    DISPATCH(ActionEvent, std::shared_ptr<ActionEvent>(new ActionEvent(2)));
    DISPATCH(PropertyChangeEvent, new PropertyChangeEvent());
  }
};

class testListener :
public LISTENER(ActionEvent), public LISTENER(PropertyChangeEvent) {
  int ID;
public:
  testListener(testDispatcher *dispatcher, int id) {
    ID = id;
//    dispatcher->addListener(this);  // ambiguous function.
//    dispatcher->addListener((EventDispatcher<ActionEvent>::EventListener *) this);  // ambiguous function.
//    ((EventDispatcher<ActionEvent> *)dispatcher)->addListener(this);  // works but is long.
    LISTEN(ActionEvent, dispatcher, this);
    if(id % 2) {
      // Only respond to property change events on odd numbered listeners just to be different.
//      dispatcher->addListener(this);  // ambiguous function.
//      ((EventDispatcher<PropertyChangeEvent> *)dispatcher)->addListener(this);  // works but is long.
      LISTEN(PropertyChangeEvent, dispatcher, this);
    }
  }

protected:
  void onEvent(EventDispatcher<ActionEvent> *source, std::shared_ptr<ActionEvent> event) {
    (void)source;
    (void)event;
    std::cout << ID << " ActionEvent " << event->actionID << std::endl;
//    event->actionID += 5;
//    std::cout << " set to " << event->actionID << std::endl;
  }

  void onEvent(EventDispatcher<PropertyChangeEvent> *source, std::shared_ptr<PropertyChangeEvent> event) {
    (void)source;
    (void)event;
    std::cout << ID << "PropertyChangeEvent" << std::endl;
  }
};

int main(int argc, char *argv[]) {
  testDispatcher td();
  testListener tl1(&td, 1);
  testListener tl2(&td, 2);
  testListener tl3(&td, 3);
  testListener tl4(&td, 4);
  td.test();

  return 0;
}

我真的很喜欢这方面的反馈。 有什么方法可以改进它吗? 我可以使模板更容易使用吗? (更好的宏?) 他们错过了任何可能的陷阱吗?

0 个答案:

没有答案