#### 两个例子
.GUI event loop ```
while (running)
{
// 从事件队列里获取一个事件
Event event = getNextEvent();
// Handle event...
}
```
.Central event bus
不同系统公用的通信中心 #### 有问题的code ```
class Audio
{
public:
static void playSound(SoundId id, int volume);
}; class Audio
{
public:
static void playSound(SoundId id, int volume);
}; class Menu
{
public:
void onSelect(int index)
{
Audio::playSound(SOUND_BLOOP, VOL_MAX);
// Other stuff...
}
}; ```
问题: . api同步调用, 阻塞到audio处理完请求
. 多个请求不能合并处理
. 处理请求有可能运行在错误的线程上(没有锁) ####模式定义:
```
一系列的通知或请求存储在先进先出的队列里. 发送通知进行入队; 请求处理者从队列里获取请求.
``` ##### 何时使用
```
.如果只是想从sender那里获取消息,使用 observer 或者command将会更简单.
.当你需要push什么到另一个模块,或者pull什么从另一个地方的时候, 你需要一个buffer, 此时就需要一个队列了.
. 队里提供的pull操作, receiver可以延迟处理,合并请求, 或者丢弃. pull请求不开放给sender使用,当sender需要获得响应的时候,队列就有点技穷了.(send then pray)
``` #### 注意事项
```
.中心事件队列是个全局变量
.世界状态会改变,(队列处理不是及时的)
.困在反馈循环里(a ->b -> a ->b ...).如果是同步队列的,你会很快的发现循环bug.
一般原则: 避免在在处理事件的函数里发送事件.
``` #### Sample Code ```
struct PlayMessage
{
SoundId id;
int volume;
}; class Audio
{
public:
static void init()
{
numPending_ = ;
} // Other stuff...
private:
static const int MAX_PENDING = ; static PlayMessage pending_[MAX_PENDING];
static int numPending_;
}; void Audio::playSound(SoundId id, int volume)
{
assert(numPending_ < MAX_PENDING); pending_[numPending_].id = id;
pending_[numPending_].volume = volume;
numPending_++;
} class Audio
{
public:
static void update()
{
for (int i = ; i < numPending_; i++)
{
ResourceId resource = loadSound(pending_[i].id);
int channel = findOpenChannel();
if (channel == -) return;
startSound(resource, channel, pending_[i].volume);
} numPending_ = ;
} // Other stuff...
}; ``` ##### ring buffer 循环buffer ```
class Audio
{
public:
static void init()
{
head_ = ;
tail_ = ;
} // Methods...
private:
static int head_;
static int tail_; // Array...
}; void Audio::playSound(SoundId id, int volume)
{
assert((tail_ + ) % MAX_PENDING != head_); // Add to the end of the list.
pending_[tail_].id = id;
pending_[tail_].volume = volume;
tail_ = (tail_ + ) % MAX_PENDING;
} void Audio::update()
{
// If there are no pending requests, do nothing.
if (head_ == tail_) return; ResourceId resource = loadSound(pending_[head_].id);
int channel = findOpenChannel();
if (channel == -) return;
startSound(resource, channel, pending_[head_].volume); head_ = (head_ + ) % MAX_PENDING;
}
``` ##### 合并请求 ```
void Audio::playSound(SoundId id, int volume)
{
// Walk the pending requests.
for (int i = head_; i != tail_;
i = (i + ) % MAX_PENDING)
{
if (pending_[i].id == id)
{
// Use the larger of the two volumes.
pending_[i].volume = max(volume, pending_[i].volume); // Don't need to enqueue.
return;
}
} // Previous code...
}
``` ##### 多线程 push pull操作需要线程安全 #### 队列里保存的是什么 ```
event or message
event queue(一对多)
描述一些已经发生的事情, 类似异步的observer模式
.允许多个监听者, 队列里保存的都是*已经发生的事件*, 发送者不关心谁去接受它.
.作用域更广.被用于广播一类的事情.趋向于全局可见.
message queue(多对一)
更趋向于只有一个监听者. 多个请求从不同的地方发来,一个处理者进行处理
``` #### 谁可以读队列 ```
单播队列:
.队列实现读取. 发送者只管发送
.队列被封装的更好
.没有读取竞争(决定是广播还是挨个分配)
广播队列:
.如果没有监听者,event被丢弃
.你可能会需要一个事件过滤
工作队列:
类似广播队列,比如worker pool
.需要调度
``` #### 谁可以写队列 ```
一个写者(类似同步observer)
.你明确知道事件是谁发出的
.通常允许多个读者
多个写者
.注意循环
.需要有访问发送者的途径(事件里包含sender的引用)
``` #### 队里里对象的生命周期 ```
. 转移所有权. 有发送者转给队列,队列转给接受者
. 共享所有权.
. 所有权只给队列. (队列申请内存,然后发送者填充数据, 接受者得到引用)
``` ###See also ```
. 队列很像是异步的observer
. message queue, pub sub
. 有限状态机, 状态机需要输入,如果你想异步执行,可以使用队列. 如果你需要多个状态机互相发消息, 需要一个queue接受输入(mail box), 这叫做actor model
. go语言内置的channel本质上就是个 事件或消息 队列.
```

15_游戏编程模式EventQueue的更多相关文章

  1. 游戏编程模式KeyNote

    [游戏编程模式KeyNote] 1.命令模式. 重做在游戏中并不常见,但重放常见.一种简单的重放实现是记录游戏每帧的状态,这样它可以回放,但那会消耗太多的内存.相反,很多游戏记录每个实体每帧运行的命令 ...

  2. 游戏编程模式 Game Programming Patterns (Robert Nystrom 著)

    第1篇 概述 第1章 架构,性能和游戏 (已看) 第2篇 再探设计模式 第2章 命令模式 (已看) 第3章 享元模式 (已看) 第4章 观察者模式 (已看) 第5章 原型模式 (已看) 第6章 单例模 ...

  3. 16_游戏编程模式ServiceLocator 服务定位

    ####简单说,就是某个系统作为一个服务,对全局系统可见. Service Locator (服务定位) ``` //简单粗暴的代码, 使用声音系统 // Use a static class? Au ...

  4. DirectX游戏编程入门

    刚开始学习D3D,安装完DirectX9后,在VS2008中新建Win32项目· ----------------------------------------------------------- ...

  5. DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)

            本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com         注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...

  6. PC游戏编程(入门篇)(前言写的很不错)

    PC游戏编程(入门篇) 第一章 基石 1. 1 BOSS登场--GAF简介 第二章 2D图形程式初体验 2.l 饮水思源--第一个"游戏"程式 2.2 知其所以然一一2D图形学基础 ...

  7. 游戏编程算法与技巧 Game Programming Algorithms and Techniques (Sanjay Madhav 著)

    http://gamealgorithms.net 第1章 游戏编程概述 (已看) 第2章 2D图形 (已看) 第3章 游戏中的线性代数 (已看) 第4章 3D图形 (已看) 第5章 游戏输入 (已看 ...

  8. 《逐梦旅程 WINDOWS游戏编程之从零开始》笔记5——Direct3D中的顶点缓存和索引缓存

    第12章 Direct3D绘制基础 1. 顶点缓存 计算机所描绘的3D图形是通过多边形网格来构成的,网网格勾勒出轮廓,然后在网格轮廓的表面上贴上相应的图片,这样就构成了一个3D模型.三角形网格是构建物 ...

  9. 3D游戏编程大师技巧──2D引擎的编译问题

    接上一篇文章,这里将介绍2D引擎的编译,从现在开始才真正进入<3D游戏编程大师技巧>的学习.本书的第一.二章只是简介了游戏编程和windows编程,从第三章开始才是介绍<window ...

随机推荐

  1. 爬虫五 Beautifulsoup模块详细

    一.基本使用 from bs4 import BeautifulSoup htmlCharset = "GB2312" soup=BeautifulSoup(html_doc,'l ...

  2. 剑指offer 面试12题

    面试12题: 题目:矩阵中的路径 题:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格 ...

  3. iOS应用生命周期

    作为应用程序的委托对象,AppDelegate类在应用生命周期的不同阶段会回调不同的方法.首先,让我们先了解一下iOS 应用的不同状态及它们彼此间的关系,见图1 . 图1 iOS应用状态图 下面简要介 ...

  4. assign,copy,retain的区别以及weak和strong的区别

    @property (nonatomic, assign) NSString *title;    什么是assign,copy,retain之间的区别?      assign: 简单赋值,不更改索 ...

  5. Capslock and Esc

    将Caps Lock转换成Esc(windows and linux) 1. linux 下将Caps Lock 转换成Esc 作为一个vimer,Caps Lock对我(还有其他很多人)来说根本就是 ...

  6. DataTable Group By或运算 Linq Aggregate的使用

    class Program { static void Main(string[] args) { DataTable dt = new DataTable(); dt.Columns.Add(&qu ...

  7. 【转载】格式化存储装置成为 Ext2/Ext3/Ext4 档案系统

    格式化 用系统管理员帐户 (即 root) 身份打「mkfs -t ext2|ext3|ext4 储存装置」: mkfs -t ext3 /dev/sdb5 要格式化档案系统为 Ext2,亦可以直接使 ...

  8. 记录python面试题

    闲来无事,记录一下曾经以及深刻的面试题 记录一下我记忆比较深的面试题,以后若用到python相关还能细细把玩 搜狐面试题: 一.写一个缓存优化策略 解答:这个题主要考察对lru_cache的理解,所以 ...

  9. Cisco学习笔记

    目录 1. 路由 1.1 静态路由 1.2 动态路由 2. 访问控制列表 2.1 标准访问控制列表 2.2 扩展访问控制列表 2.3 命名访问控制列表 3. VLAN 3.1 基础知识 3.2 配置实 ...

  10. 【转载】如何简单地理解Python中的if __name__ == '__main__'

    原帖:https://blog.csdn.net/yjk13703623757/article/details/77918633 通俗的理解__name__ == '__main__':假如你叫小明. ...