双缓冲(double buffering)的原理和使用

      最近在使用C#做项目的时候发现在加载一个具有大量控件和复杂背景图形的窗口时,会出现很影响视觉体验的频闪问题。最后发现使用双缓冲能很好的解决这个问题。下面就介绍一下双缓冲的原理及使用。

一、双缓冲作用
     
      双缓 冲甚至是多缓冲,在许多情况下都很有用。一般需要使用双缓冲区的地方都是由于“生产者”和“消费者”供需不一致所造成的。这样的情况在很多地方后可能会发生,使用 多缓冲可以很好的解决。我举几个常见的例子:

     例 1. 在网络传输过程中数据的接收,有时可能数据来的太快来不及接收导致数据丢失。这是由于“发送者”和“接收者”速度不一致所致,在他们之间安排一个或多个缓冲区 来存放来不及接收的数据,让速度较慢的“接收者”可以慢慢地取完数据不至于丢失。

      例2. 再如,计算机中的三级缓存结构:外存(硬盘)、内存、高速缓存(介于CPU和内存之间,可能由多级)。从左到右他们的存储容量不断减小,但速度不断提升,当然价格也是 越来越贵。作为“生产者”的 CPU 处理速度很快,而内存存取速度相对CPU较慢,如果直接在内存中存取数据,他们的速度不一致会导致 CPU  能力下降。因此在他们之间又增加的 高速缓存来作为缓冲区平衡二者速度上的差异。

      例3. 在图形图像显示过程中,计算机从显示缓冲区取数据然后显示,很多图形的操作都很复杂需要大量的计算,很难访问一次显示缓冲区就能写入待显示的完整图形数据,通常需要 多次访问显示缓冲区,每次访问时写入最新计算的图形数据。而这样造成的后果是一个需要复杂计算的图形,你看到的效果可能是一部分一部分地显示出来的,造成很大的闪烁不连贯。 而使用双缓冲,可以使你先将计算的中间结果存放在另一个缓冲区中,但全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区。

      例1 中使用双缓冲是为了防止数据丢失,例2 中使用双缓冲是为了提高 CPU 的处理效率,而例3使用双缓冲是为了防止显示图形时的闪烁延迟等不良体验。

二、双缓冲原理

     这里,主要以双缓冲在图形图像显示中的应用做说明。  
     
    上面例3中提到了双缓冲的主要原理,这里通过一个图再次理解一下:

     图 1  双缓冲示意图

     注意,显示缓冲区是和显示器一起的,显示器只负责从显示缓冲区取数据显示。我们通常所说的在显示器上画一条直线,其实就是往该显示缓冲区中写入数据。 显示器通过不断的刷新(从显示缓冲区取数据),从而使显示缓冲区中数据的改变及时的反映到显示器上。

     这也是显示复杂图形时造成延迟的原因,比如你现在要显示从屏幕中心向外发射的一簇射线,你开始编写代码用一个循环从0度开始到360度,每隔一定角度画一条从圆 心开始向外的直线。你每次画线其实是往显示缓冲区写入数据,如果你还没有画完,显示器就从显示缓冲区取数据显示图形,此时你看到的是一个不完整的图形,然后你 继续画线,等到显示器再次取显示缓冲区数据显示时,图形比上次完整了一些,依次下去直到显示完整的图形。你看到图形不是一次性完整地显示出来,而是每次显示一部分,从而造成闪烁。

     原理懂了,看下 demo 就知道怎么用了。下面先介绍 C# 中如何使用双缓冲,其他环境下由于没有用到所以没写,等用到了再在下面补充,不过其他环境下 过程也基本相似。
     
三、双缓冲使用 

[csharp]  view plain  copy
  1. public void Show(System.Windows.Forms.Control control)  
  2. {  
  3.     Graphics gc = control.CreateGraphics();  
  4.     // 创建缓冲图形上下文 (类似 Win32 中的CreateCompatibleDC)  
  5.     BufferedGraphicsContext dc = new BufferedGraphicsContext();   
  6.     // 创建指定大小缓冲区 (类似 Win32 中的 CreateCompatibleBitmap)  
  7.     BufferedGraphics backBuffer = dc.Allocate(gc, new Rectangle(new Point(0, 0), control.Size));       
  8.     gc = backBuffer.Graphics;             // 获取缓冲区画布          
  9.     /* 像使用一般的 Graphics 一样绘图 */  
  10.     Pen pen = new Pen(Color.Gray);  
  11.     foreach (Step s in m_steps)  
  12.     {  
  13.         gc.DrawLine(pen, s.Start, s.End);  
  14.     }  
  15.       
  16.     // 将双缓冲区中的图形渲染到指定画布上 (类似 Win32 中的)BitBlt  
  17.     backBuffer.Render(control.CreateGraphics());     
  18. }  

原文出自: http://blog.csdn.net/xiaohui_hubei/article/details/16319249

你可能感兴趣的:(双缓冲(double buffering)的原理和使用)