序列模式-双缓冲模式
参考章节:https://gpp.tkchu.me/double-buffer.html
脑内画面
双缓冲模式准备两份数据:一份给外界稳定读取,一份在幕后写入。写完后交换两者。它像剧场换景:观众看到的是前台布景,工作人员在后台准备下一幕,准备好后一次性切换。
#mermaid-svg-SiV5Z2jRn1XioyC4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SiV5Z2jRn1XioyC4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SiV5Z2jRn1XioyC4 .error-icon{fill:#552222;}#mermaid-svg-SiV5Z2jRn1XioyC4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SiV5Z2jRn1XioyC4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SiV5Z2jRn1XioyC4 .marker.cross{stroke:#333333;}#mermaid-svg-SiV5Z2jRn1XioyC4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SiV5Z2jRn1XioyC4 p{margin:0;}#mermaid-svg-SiV5Z2jRn1XioyC4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SiV5Z2jRn1XioyC4 .cluster-label text{fill:#333;}#mermaid-svg-SiV5Z2jRn1XioyC4 .cluster-label span{color:#333;}#mermaid-svg-SiV5Z2jRn1XioyC4 .cluster-label span p{background-color:transparent;}#mermaid-svg-SiV5Z2jRn1XioyC4 .label text,#mermaid-svg-SiV5Z2jRn1XioyC4 span{fill:#333;color:#333;}#mermaid-svg-SiV5Z2jRn1XioyC4 .node rect,#mermaid-svg-SiV5Z2jRn1XioyC4 .node circle,#mermaid-svg-SiV5Z2jRn1XioyC4 .node ellipse,#mermaid-svg-SiV5Z2jRn1XioyC4 .node polygon,#mermaid-svg-SiV5Z2jRn1XioyC4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SiV5Z2jRn1XioyC4 .rough-node .label text,#mermaid-svg-SiV5Z2jRn1XioyC4 .node .label text,#mermaid-svg-SiV5Z2jRn1XioyC4 .image-shape .label,#mermaid-svg-SiV5Z2jRn1XioyC4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-SiV5Z2jRn1XioyC4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SiV5Z2jRn1XioyC4 .rough-node .label,#mermaid-svg-SiV5Z2jRn1XioyC4 .node .label,#mermaid-svg-SiV5Z2jRn1XioyC4 .image-shape .label,#mermaid-svg-SiV5Z2jRn1XioyC4 .icon-shape .label{text-align:center;}#mermaid-svg-SiV5Z2jRn1XioyC4 .node.clickable{cursor:pointer;}#mermaid-svg-SiV5Z2jRn1XioyC4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SiV5Z2jRn1XioyC4 .arrowheadPath{fill:#333333;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SiV5Z2jRn1XioyC4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SiV5Z2jRn1XioyC4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SiV5Z2jRn1XioyC4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SiV5Z2jRn1XioyC4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SiV5Z2jRn1XioyC4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SiV5Z2jRn1XioyC4 .cluster text{fill:#333;}#mermaid-svg-SiV5Z2jRn1XioyC4 .cluster span{color:#333;}#mermaid-svg-SiV5Z2jRn1XioyC4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SiV5Z2jRn1XioyC4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SiV5Z2jRn1XioyC4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-SiV5Z2jRn1XioyC4 .icon-shape,#mermaid-svg-SiV5Z2jRn1XioyC4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SiV5Z2jRn1XioyC4 .icon-shape p,#mermaid-svg-SiV5Z2jRn1XioyC4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SiV5Z2jRn1XioyC4 .icon-shape .label rect,#mermaid-svg-SiV5Z2jRn1XioyC4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SiV5Z2jRn1XioyC4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SiV5Z2jRn1XioyC4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SiV5Z2jRn1XioyC4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 读者看到
Current Buffer
交换引用
系统写入
Next Buffer
下一帧读者看到
刚写好的 Buffer
它解决的问题
如果渲染器正在读取一张画面,而游戏逻辑同时往里面写,玩家可能看到半更新的状态。双缓冲通过"读一份、写一份、最后交换"保证外界看到的永远是完整快照。
C# 示例
csharp
public sealed class GridBuffer
{
private readonly char[,] _cells;
public int Width => _cells.GetLength(0);
public int Height => _cells.GetLength(1);
public GridBuffer(int width, int height)
{
_cells = new char[width, height];
Clear('.');
}
public char this[int x, int y]
{
get => _cells[x, y];
set => _cells[x, y] = value;
}
public void Clear(char value)
{
for (var x = 0; x < Width; x++)
for (var y = 0; y < Height; y++)
{
_cells[x, y] = value;
}
}
}
public sealed class DoubleBufferedGrid
{
private GridBuffer _current;
private GridBuffer _next;
public DoubleBufferedGrid(int width, int height)
{
_current = new GridBuffer(width, height);
_next = new GridBuffer(width, height);
}
public GridBuffer Current => _current;
public GridBuffer Next => _next;
public void DrawNextFrame(IEnumerable<(int X, int Y, char Icon)> sprites)
{
_next.Clear('.');
foreach (var sprite in sprites)
{
_next[sprite.X, sprite.Y] = sprite.Icon;
}
}
public void Swap()
{
(_current, _next) = (_next, _current);
}
}
什么时候用
- 渲染、模拟、寻路、可见性数据需要稳定快照。
- 读者不应看到写入过程中的中间状态。
- 同一帧内有多个系统写入,最终要一次性提交结果。
使用时的锋利边
双缓冲会让内存翻倍,也会引入"交换时机"的规则。它适合数据块整体替换,不适合每次只改几个字段的小对象。若数据很大,可以只双缓冲索引、命令列表或脏区域。