消息的所有权,使用哪种设计模式?
Ownership of messages, Which Designpattern to use?
我有一个系统,它从某个地方(在本例中为网络)接收消息(具有某种类型的数据块)。一旦收到,它们就会存储在队列中。然后这些消息应该被分派到处理函数,这必须很快发生(很多消息)
目前系统的设计方式是,每个消息类型都是自己的 class 并覆盖一个虚拟函数 运行(Handler&),它基本上在处理程序中调用正确的方法。例如:
class PingMessage: public Message{
... // Some member variables
void run(Handler& handler){
handler.handlePing(*this);
}
}
class Handler{
void handlePing(const PingMessage& msg){...}
}
在这个设计中,队列在消息被分派后删除消息。问题是:一些处理函数需要存储消息以便稍后执行它们。复制消息不仅浪费内存和时间(它在发送后立即被删除)而且有时也是不可能的(复杂的反序列化数据结构)所以最好是将所有权传递给处理程序。
我有一种感觉,这有一个设计模式或最佳实践。但是我找不到。
我可以想象的是调用通用处理程序函数 "handleMessage(Type, Message*)",该函数打开类型并使用消息 static_cast 发送到正确的类型。那么通过传递指针的约定就很清楚了,处理程序负责删除消息。甚至可以使用一个基础 class,它可以进行切换并实现所有处理函数为空。如果处理程序函数 returns 为真,则 handleMessage 函数会删除消息,否则它假定被调用者将其存储在某处。但我不确定这是否是正确的方法,或者它是否会产生很多开销。似乎有太多错误的余地。
特别是因为我必须对消息类型进行 2 次检查:一项用于选择正确的 class 进行反序列化,另一项用于调用正确的函数。
注意:没有可用的 C++11。
旁注:还有一些其他的东西:大多数处理程序只是处理消息。因此,使用 new 在堆上创建它并在此之后立即释放它可能非常慢(主要是只有几个字节的非常小的消息)使用处理程序,将消息反序列化为基于堆栈的对象会更好,但我'我必须再次复制它们,但我不能。那么我是否应该将原始消息传递给特定的处理函数,让他们按照自己的意愿进行反序列化?这意味着不同的处理程序有很多重复的代码...在这里做什么???
即使您表明您没有 C++11,也不需要大量代码来实现您自己的 C++03 兼容 std::shared_ptr。如果您的应用程序不是多线程的,您甚至不需要担心以线程安全的方式更新对象的引用计数。
我不使用 Boost,所以我不能权威地说,但 Boost 可能已经有一个兼容 C++03 的 std::shared_ptr 实现,您可以使用。
内存分配器的大多数现代实现实际上非常高效,在堆上实例化一个新的消息对象并不像您想象的那么重要。
所以,你的总体做法是:
您收到消息,并在堆上实例化 Message
的适当子类。
运行() 方法还应接收消息本身的引用计数句柄,并将其传递给处理程序。
如果处理程序不需要保存消息的副本,它什么都不做,它很快就会被销毁,否则它会获取引用句柄,并将其隐藏在某个地方。
我有一个系统,它从某个地方(在本例中为网络)接收消息(具有某种类型的数据块)。一旦收到,它们就会存储在队列中。然后这些消息应该被分派到处理函数,这必须很快发生(很多消息)
目前系统的设计方式是,每个消息类型都是自己的 class 并覆盖一个虚拟函数 运行(Handler&),它基本上在处理程序中调用正确的方法。例如:
class PingMessage: public Message{
... // Some member variables
void run(Handler& handler){
handler.handlePing(*this);
}
}
class Handler{
void handlePing(const PingMessage& msg){...}
}
在这个设计中,队列在消息被分派后删除消息。问题是:一些处理函数需要存储消息以便稍后执行它们。复制消息不仅浪费内存和时间(它在发送后立即被删除)而且有时也是不可能的(复杂的反序列化数据结构)所以最好是将所有权传递给处理程序。
我有一种感觉,这有一个设计模式或最佳实践。但是我找不到。
我可以想象的是调用通用处理程序函数 "handleMessage(Type, Message*)",该函数打开类型并使用消息 static_cast 发送到正确的类型。那么通过传递指针的约定就很清楚了,处理程序负责删除消息。甚至可以使用一个基础 class,它可以进行切换并实现所有处理函数为空。如果处理程序函数 returns 为真,则 handleMessage 函数会删除消息,否则它假定被调用者将其存储在某处。但我不确定这是否是正确的方法,或者它是否会产生很多开销。似乎有太多错误的余地。
特别是因为我必须对消息类型进行 2 次检查:一项用于选择正确的 class 进行反序列化,另一项用于调用正确的函数。
注意:没有可用的 C++11。
旁注:还有一些其他的东西:大多数处理程序只是处理消息。因此,使用 new 在堆上创建它并在此之后立即释放它可能非常慢(主要是只有几个字节的非常小的消息)使用处理程序,将消息反序列化为基于堆栈的对象会更好,但我'我必须再次复制它们,但我不能。那么我是否应该将原始消息传递给特定的处理函数,让他们按照自己的意愿进行反序列化?这意味着不同的处理程序有很多重复的代码...在这里做什么???
即使您表明您没有 C++11,也不需要大量代码来实现您自己的 C++03 兼容 std::shared_ptr。如果您的应用程序不是多线程的,您甚至不需要担心以线程安全的方式更新对象的引用计数。
我不使用 Boost,所以我不能权威地说,但 Boost 可能已经有一个兼容 C++03 的 std::shared_ptr 实现,您可以使用。
内存分配器的大多数现代实现实际上非常高效,在堆上实例化一个新的消息对象并不像您想象的那么重要。
所以,你的总体做法是:
您收到消息,并在堆上实例化
Message
的适当子类。运行() 方法还应接收消息本身的引用计数句柄,并将其传递给处理程序。
如果处理程序不需要保存消息的副本,它什么都不做,它很快就会被销毁,否则它会获取引用句柄,并将其隐藏在某个地方。