欢迎光临散文网 会员登陆 & 注册

《游戏编程模式》笔记——事件队列

2023-09-18 16:55 作者:黑白色的枫  | 我要投稿

意图

解耦发出消息或事件的时间和处理它的时间。

模式

事件队列在队列中按先入先出的顺序存储一系列通知或请求。发送通知时,将请求放入队列兵返回。处理请求的系统之后稍晚从队列中获取请求并处理。这解耦了发送者和接受者,既静态又及时。

何时使用

如果只想解耦接受者和发送者,观察者模式和命令模式都可以用较小的复杂度进行处理。

在解耦某些需要及时处理的东西时使用队列。

队列将控制权交给了发送者,接受者可以延迟处理,合并或者忽视请求。发送者能做的就是向队列发送请求,当发送者需要回复时,队列不是好选择。

设计决策

队列中存储了什么?

如果存储事件:

“事件”或者“通知”描绘已经发生的事情。入队事件,其他对象可以对这个事件作出回应。

可能会允许多个监听者。队列包含的是已经发生的事情,发送者可能不关心谁接受它。

访问队列的模块更广。事件队列通常广播事件到任何感兴趣的部分,为了最大程度允许哪些部分能感兴趣,队列一般是全局可见的。

如果存储消息:

“消息”或“请求”描绘了想要发生在未来的事情,比如“播放声音”。可以视其为服务的异步API。

更可能只有一个监听者。假如存储的消息只请求音频API播放声音,如果引擎的随便一个地方都能从队列中拿走消息就不好了。

谁能从队列中读取?

单播队列:

在队列是类API的一部分时,单播是自然的。

队列变成读取者的实现细节。发送者知道的所有事就是发条信息。

队列更封装。

不用担心监听者之间的竞争。使用多个监听者,就需要决定队列中的事物是一对多的分给全部监听者,还是一对一分给单独的监听者。这两种情况下,监听者最终要么做了多余的事要么在互相干扰。使用单一监听者这种复杂性就消失了。

广播队列:

这是大多数“事件”系统工作的方法。一个事件进来,所有监听者都能收到这个事件。

但事件也可能无人接收。零个监听者时,事件就会消失。

也许需要过滤事件。广播队列对程序的所有部分可见,大多数广播事件让监听者筛出其需要接收的事件。

工作队列:

类似广播队列,有多个监听器。不同之处在于每个队列的东西只会投到监听器其中的一个。常用于将工作打包给同时运行的线程池。

但是需要规划。由于一个事物只有一个监听器,队列逻辑需要指出最好的选项。可以像round robin算法或者乱序选择一样简单,或者可以使用更加复杂的优先度系统。

谁能写入队列?

队列模式兼容所有可能的读写设置:一对一、一对多、多对一、多对多。

使用单个写入器:

这种风格和同步的观察者模式很像,有特定对象收集所有可接受的事件。

隐式知道事件是从哪来的。由于只有一个对象可以向队列添加事件,任何监听器都可以圈圈的假设那就是发送者。

通常运行有多个读取者。可以使用单发送者对单接收者的队列,但这样沟通系统更像纯粹的队列数据结构。

使用多个写入器:

代码的任何部分都能给队列添加请求,“全局”或“中心”事件总线像这样工作。

要小心环路。可能会在处理事件时添加事件,导致触发反馈循环。

很可能需要在事件中添加对发送者的引用。监听者接到事件时不知道是谁发送的,如果需要知道发送者,需要将发送者打包到事件对象中。

对象在队列中的生命周期如何?

传递所有权:

手动管理内存的传统方式。消息入队时,队列拥有了它,发送者不再拥有它。当它被处理时,接受者获取了所有权,负责销毁它。

共享所有权:

对于拥有垃圾回收机制的语言,消息只要有地方引用就会存在,没有引用时自动释放。

队列拥有它:

让消息永远存在队列中。发送者不再自己分配消息内存,队列返回一个队列中已经在内存的消息的引用,接收者这引用队列中相同的消息。换言之,队列存储的背后是对象池模式。


队列模式是观察者模式的异步实现。

消息队列是队列模式的更高级实现,事件队列在应用中,消息队列通常在应用间交流。

很像GoF的状态模式中的确定状态机,需要一个输入流。如果想要异步响应。可以考虑队列存储它们。当一对状态机相互发送消息时,每个状态机都有一个小的未处理队列,需要重新发明actor model。

Go语言内建的“通道”类型本质上是事件队列或消息队列。


参考

《游戏编程模式》

《游戏编程模式》笔记——事件队列的评论 (共 条)

分享到微博请遵守国家法律