Performance C+中高效的消息工厂和处理程序+;

Performance C+中高效的消息工厂和处理程序+;,performance,c++11,handler,message,Performance,C++11,Handler,Message,我们公司正在用C++11重写大部分遗留C代码。(这也意味着我是学习C++的C程序员)。我需要关于消息处理程序的建议 我们有分布式系统-服务器进程通过TCP向客户端进程发送压缩消息 在C代码中,这样做: -根据类型和子类型解析消息,这两个字段始终是前两个字段 - call a handler as handler[type](Message *msg) - handler creates temporary struct say, tmp_struct to hold the parsed va

我们公司正在用C++11重写大部分遗留C代码。(这也意味着我是学习C++的C程序员)。我需要关于消息处理程序的建议

我们有分布式系统-服务器进程通过TCP向客户端进程发送压缩消息

在C代码中,这样做: -根据类型和子类型解析消息,这两个字段始终是前两个字段

- call a handler as handler[type](Message *msg)

- handler creates temporary struct say, tmp_struct to hold the parsed values and .. 

- calls subhandler[type][subtype](tmp_struct)
每个类型/子类型只有一个处理程序

移动到C++11和多线程环境。我的基本想法是-

1) 为每个类型/子类型组合注册处理器对象。这是
实际上是向量的向量- 向量<向量>

class MsgProcessor {

    // Factory function
    virtual Message *create();
    virtual Handler(Message *msg)
}
这将由不同的消息处理器继承

class AMsgProcessor : public MsgProcessor {

      Message *create() override();
      handler(Message *msg);
}
2) 使用查找向量向量的方法获取处理器。 使用重载的create()工厂函数获取消息。 因此,我们可以将实际消息和解析的值保存在消息中

3) 现在有一点黑客攻击,这个消息应该被发送到其他线程进行繁重的处理。为了避免再次在向量中查找,在消息中添加了指向proc的指针

class Message {
    const MsgProcessor *proc; // set to processor, 
                              // which we got from the first lookup
                              // to get factory function.
};
所以其他线程就可以了

Message->proc->Handler(Message *);
这看起来很糟糕,但希望这将有助于将消息处理程序与工厂分离。这适用于多个类型/子类型希望创建相同消息,但处理方式不同的情况

我在搜索时发现:

它提供了一种将消息与处理程序完全分离的方法。但我想知道我上面的简单方案是否会被认为是一个可接受的设计。这也是实现我想要的目标的错误方式吗


与速度一样,效率是此应用程序最重要的要求。我们已经在做两个内存Jumbs=>2向量+虚拟函数调用来创建消息。处理程序有两种尊重,我想从缓存的角度来看,这并不好。

虽然您的要求不明确,但我认为我有一种设计可能正是您所需要的

查看完整的示例

它有以下组成部分:

  • 消息处理器的接口类
    IMessageProcessor
  • 表示消息的基类<代码>消息
  • 一种注册类,本质上是一个单例,用于存储与(类型,子类型)对相对应的消息处理器<代码>注册器。它将映射存储在无序的映射中。您还可以稍微调整它以获得更好的性能。
    registor
    的所有公开API都受
    std::mutex
    的保护
  • MessageProcessor的具体实现<在这种情况下,代码>AMSG处理器和
    BMsgProcessor
  • 模拟
    函数,以显示它是如何组合在一起的
  • 在此处粘贴代码:

    /*
     * http://stackoverflow.com/questions/40230555/efficient-message-factory-and-handler-in-c
     */
    
    #include <iostream>
    #include <vector>
    #include <tuple>
    #include <mutex>
    #include <memory>
    #include <cassert>
    #include <unordered_map>
    
    class Message;
    
    class IMessageProcessor
    {
    public:
      virtual Message* create() = 0;
      virtual void handle_message(Message*) = 0;
      virtual ~IMessageProcessor() {};
    };
    
    /*
     * Base message class
     */
    class Message
    {
    public:
      virtual void populate() = 0;
      virtual ~Message() {};
    };
    
    using Type = int;
    using SubType = int;
    using TypeCombo = std::pair<Type, SubType>;
    using IMsgProcUptr = std::unique_ptr<IMessageProcessor>;
    
    /*
     * Registrator class maintains all the registrations in an
     * unordered_map.
     * This class owns the MessageProcessor instance inside the
     * unordered_map.
     */
    class Registrator
    {
    public:
      static Registrator* instance();
    
      // Diable other types of construction
      Registrator(const Registrator&) = delete;
      void operator=(const Registrator&) = delete;
    
    public:
      // TypeCombo assumed to be cheap to copy
      template <typename ProcT, typename... Args>
      std::pair<bool, IMsgProcUptr> register_proc(TypeCombo typ, Args&&... args)
      {
        auto proc = std::make_unique<ProcT>(std::forward<Args>(args)...);
        bool ok;
        {
          std::lock_guard<std::mutex> _(lock_);
          std::tie(std::ignore, ok) = registrations_.insert(std::make_pair(typ, std::move(proc)));
        }
        return (ok == true) ? std::make_pair(true, nullptr) : 
                              // Return the heap allocated instance back
                              // to the caller if the insert failed.
                              // The caller now owns the Processor
                              std::make_pair(false, std::move(proc));
      }
    
      // Get the processor corresponding to TypeCombo
      // IMessageProcessor passed is non-owning pointer
      // i.e the caller SHOULD not delete it or own it
      std::pair<bool, IMessageProcessor*> processor(TypeCombo typ)
      {
        std::lock_guard<std::mutex> _(lock_);
    
        auto fitr = registrations_.find(typ);
        if (fitr == registrations_.end()) {
          return std::make_pair(false, nullptr);
        }
        return std::make_pair(true, fitr->second.get());
      }
    
      // TypeCombo assumed to be cheap to copy
      bool is_type_used(TypeCombo typ)
      {
        std::lock_guard<std::mutex> _(lock_);
        return registrations_.find(typ) != registrations_.end();
      }
    
      bool deregister_proc(TypeCombo typ)
      {
        std::lock_guard<std::mutex> _(lock_);
        return registrations_.erase(typ) == 1;
      }
    
    private:
      Registrator() = default;
    
    private:
      std::mutex lock_;
      /*
       * Should be replaced with a concurrent map if at all this
       * data structure is the main contention point (which I find
       * very unlikely).
       */
      struct HashTypeCombo
      {
      public:
        std::size_t operator()(const TypeCombo& typ) const noexcept
        {
          return std::hash<decltype(typ.first)>()(typ.first) ^ 
                 std::hash<decltype(typ.second)>()(typ.second);
        }
      };
    
      std::unordered_map<TypeCombo, IMsgProcUptr, HashTypeCombo> registrations_;
    };
    
    Registrator* Registrator::instance()
    {
      static Registrator inst;
      return &inst;
      /*
       * OR some other DCLP based instance creation
       * if lifetime or creation of static is an issue
       */
    }
    
    
    // Define some message processors
    
    class AMsgProcessor final : public IMessageProcessor
    {
    public:
      class AMsg final : public Message 
      {
      public:
        void populate() override {
          std::cout << "Working on AMsg\n";
        }
    
        AMsg() = default;
        ~AMsg() = default;
      };
    
      Message* create() override
      {
        std::unique_ptr<AMsg> ptr(new AMsg);
        return ptr.release();
      }
    
      void handle_message(Message* msg) override
      {
        assert (msg);
        auto my_msg = static_cast<AMsg*>(msg);
    
        //.... process my_msg ?
        //.. probably being called in some other thread
        // Who owns the msg ??
        (void)my_msg; // only for suppressing warning
    
        delete my_msg;
    
        return;
      }
    
      ~AMsgProcessor();
    };
    
    AMsgProcessor::~AMsgProcessor()
    {
    }
    
    class BMsgProcessor final : public IMessageProcessor
    {
    public:
      class BMsg final : public Message
      {
      public:
        void populate() override {
          std::cout << "Working on BMsg\n";
        }
    
        BMsg() = default;
        ~BMsg() = default;
      };
    
      Message* create() override
      {
        std::unique_ptr<BMsg> ptr(new BMsg);
        return ptr.release();
      }
    
      void handle_message(Message* msg) override
      {
        assert (msg);
        auto my_msg = static_cast<BMsg*>(msg);
    
        //.... process my_msg ?
        //.. probably being called in some other thread
        //Who owns the msg ??
        (void)my_msg; // only for suppressing warning
    
        delete my_msg;
    
        return;
      }
    
      ~BMsgProcessor();
    };
    
    BMsgProcessor::~BMsgProcessor()
    {
    }
    
    
    TypeCombo read_from_network()
    {
      return {1, 2};
    }
    
    
    struct ParsedData {
    };
    
    Message* populate_message(Message* msg, ParsedData& pdata)
    {
      // Do something with the message
      // Calling a dummy populate method now
      msg->populate();
      (void)pdata;
      return msg;
    }
    
    void simulate()
    {
      TypeCombo typ = read_from_network();
      bool ok;
      IMessageProcessor* proc = nullptr;
    
      std::tie(ok, proc) = Registrator::instance()->processor(typ);
      if (!ok) {
        std::cerr << "FATAL!!!" << std::endl;
        return;
      }
    
      ParsedData parsed_data;
      //..... populate parsed_data here ....
    
      proc->handle_message(populate_message(proc->create(), parsed_data));
      return;
    }
    
    
    int main() {
    
      /*
       * TODO: Not making use or checking the return types after calling register
       * its a must in production code!!
       */
      // Register AMsgProcessor
      Registrator::instance()->register_proc<AMsgProcessor>(std::make_pair(1, 1));
      Registrator::instance()->register_proc<BMsgProcessor>(std::make_pair(1, 2));
    
      simulate();
    
      return 0;
    }
    
    /*
    * http://stackoverflow.com/questions/40230555/efficient-message-factory-and-handler-in-c
    */
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    类消息;
    类IMessageProcessor
    {
    公众:
    虚拟消息*create()=0;
    虚空句柄_消息(消息*)=0;
    虚拟~IMessageProcessor(){};
    };
    /*
    *基本消息类
    */
    类消息
    {
    公众:
    虚空填充()=0;
    虚拟~Message(){};
    };
    使用Type=int;
    使用SubType=int;
    使用TypeCombo=std::pair;
    使用IMsgProcUptr=std::unique\u ptr;
    /*
    *Registrator类维护一个数据库中的所有注册
    *无序的地图。
    *此类拥有内部的MessageProcessor实例
    *无序的地图。
    */
    班级登记员
    {
    公众:
    静态注册器*instance();
    //其他类型的建筑
    注册人(常量注册人&)=删除;
    无效运算符=(常量注册器&)=删除;
    公众:
    //假设复制TypeCombo很便宜
    模板
    std::pair register_proc(类型组合类型、参数和参数)
    {
    auto proc=std::make_unique(std::forward(args)…);
    布尔ok;
    {
    标准:锁紧装置(锁紧装置);
    std::tie(std::ignore,ok)=注册插入(std::make_pair(typ,std::move(proc));
    }
    返回(ok==true)?std::make_pair(true,nullptr):
    //返回堆分配的实例
    //如果插入失败,则发送给调用方。
    //调用方现在拥有处理器
    std::make_pair(false,std::move(proc));
    }
    //获取TypeCombo对应的处理器
    //传递的IMessageProcessor是非所有者指针
    //也就是说,呼叫方不应该删除它或拥有它
    std::成对处理器(类型组合类型)
    {
    标准:锁紧装置(锁紧装置);
    自动筛选=注册查找(典型);
    如果(fitr==注册次数uu.end()){
    返回std::make_pair(false,nullptr);
    }
    返回std::make_pair(true,fitr->second.get());
    }
    //假设复制TypeCombo很便宜
    bool是使用的类型(类型组合类型)
    {
    标准:锁紧装置(锁紧装置);
    返回注册\查找(典型)!=注册\结束();
    }
    bool注销程序(类型组合类型)
    {
    标准:锁紧装置(锁紧装置);
    返回注册擦除(典型)=1;
    }
    私人:
    Registrator()=默认值;
    私人:
    std::互斥锁;
    /*
    *如果出现这种情况,则应替换为并发映射
    *数据结构是主要的争论点(我发现
    *不太可能)。
    */
    结构HashTypeCombo
    {
    公众:
    std::size\u t运算符()(const TypeCombo&typ)const noexcept
    {
    返回std::hash()
    std::hash()(典型秒);
    }
    };
    std::无序地图注册;
    };
    注册者*注册者::实例()
    {
    静态注册仪器;
    返回和安装;
    /*
    *或者其他基于DCLP的实例创建
    *如果存在生存期或静态数据的创建问题
    */
    }
    //定义一些消息处理器
    AMsgProcessor final类:公共IMessageProcessor
    {
    公众:
    AMsg级决赛