转自:http://blog.csdn.net/acs713/article/details/16359551

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

一、双缓冲作用

双缓冲甚至是多缓冲,在许多情况下都很有用。一般需要使用双缓冲区的地方都是由于“生产者”和“消费者”供需不一致所造成的。这样的情况在很多地方后可能会发生,使用多缓冲可以很好的解决。我举几个常见的例子:
例 1. 在网络传输过程中数据的接收,有时可能数据来的太快来不及接收导致数据丢失。这是由于“发送者”和“接收者”速度不一致所致,在他们之间安排一个或多个缓冲区来存放来不及接收的数据,让速度较慢的“接收者”可以慢慢地取完数据不至于丢失。
 例2. 再如,计算机中的三级缓存结构:外存(硬盘)、内存、高速缓存(介于CPU和内存之间,可能由多级)。从左到右他们的存储容量不断减小,但速度不断提升,当然价格也是越来越贵。作为“生产者”的 CPU 处理速度很快,而内存存取速度相对CPU较慢,如果直接在内存中存取数据,他们的速度不一致会导致 CPU  能力下降。因此在他们之间又增加的高速缓存来作为缓冲区平衡二者速度上的差异。
例3. 在图形图像显示过程中,计算机从显示缓冲区取数据然后显示,很多图形的操作都很复杂需要大量的计算,很难访问一次显示缓冲区就能写入待显示的完整图形数据,通常需要多次访问显示缓冲区,每次访问时写入最新计算的图形数据。而这样造成的后果是一个需要复杂计算的图形,你看到的效果可能是一部分一部分地显示出来的,造成很大的闪烁不连贯。而使用双缓冲,可以使你先将计算的中间结果存放在另一个缓冲区中,但全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区。
例1 中使用双缓冲是为了防止数据丢失,例2 中使用双缓冲是为了提高 CPU 的处理效率,而例3使用双缓冲是为了防止显示图形时的闪烁延迟等不良体验。
二、双缓冲原理
这里,主要以双缓冲在图形图像显示中的应用做说明。  
上面例3中提到了双缓冲的主要原理,这里通过一个图再次理解一下:
    图 1  双缓冲示意图
注意,显示缓冲区是和显示器一起的,显示器只负责从显示缓冲区取数据显示。我们通常所说的在显示器上画一条直线,其实就是往该显示缓冲区中写入数据。显示器通过不断的刷新(从显示缓冲区取数据),从而使显示缓冲区中数据的改变及时的反映到显示器上。
这也是显示复杂图形时造成闪烁的原因,比如你现在要显示从屏幕中心向外发射的一簇射线,你开始编写代码用一个循环从0度开始到360度,每隔一定角度画一条从圆心开始向外的直线。你每次画线其实是往显示缓冲区写入数据,如果你还没有画完,显示器就从显示缓冲区取数据显示图形,此时你看到的是一个不完整的图形,然后你继续画线,等到显示器再次取显示缓冲区数据显示时,图形比上次完整了一些,依次下去直到显示完整的图形。你看到图形不是一次性完整地显示出来,而是每次显示一部分,从而造成闪烁。
原理懂了,看下 demo 就知道怎么用了。下面先介绍 Win32 API 和 C# 中如何使用双缓冲,其他环境下由于没有用到所以没写,等用到了再在下面补充,不过其他环境下过程也基本相似。
三、双缓冲使用 (Win32 版本)
[cpp] view plaincopyprint?
  1. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3. HDC hDC, hDCMem;
  4. HBITMAP hBmpMem, hPreBmp;
  5. switch (message)
  6. {
  7. case WM_PAINT:
  8. hDC = BeginPaint(hWnd, &ps);
  9. /* 创建双缓冲区 */
  10. // 创建与当前DC兼容的内存DC
  11. hDCMem = CreateCompatibleDC(hDC);
  12. // 创建一块指定大小的位图
  13. hBmpMem = CreateCompatibleBitmap(hDC, rect.right, rect.bottom);
  14. // 将该位图选入到内存DC中,默认是全黑色的
  15. hPreBmp = SelectObject(hDCMem, hMemBmp);
  16. /* 在双缓冲中绘图 */
  17. // 加载背景位图
  18. hBkBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
  19. hBrush = CreatePatternBrush(hBkBmp);
  20. GetClientRect(hWnd, &rect);
  21. FillRect(hDCMem, &rect, hBrush);
  22. DeleteObject(hBrush);
  23. /* 将双缓冲区图像复制到显示缓冲区 */
  24. BitBlt(hDC, 0, 0, rect.right, rect.bottom, hDCMem, 0, 0, SRCCOPY);
  25. /* 释放资源 */
  26. SelectObject(hDCMem, hPreBmp);
  27. DeleteObject(hMemBmp);
  28. DeleteDC(hDCMem);
  29. EndPaint(hWnd, &ps);
  30. break;
  31. }
  32. }
[cpp] view plaincopy print?
  1. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3. HDC hDC, hDCMem;
  4. HBITMAP hBmpMem, hPreBmp;
  5. switch (message)
  6. {
  7. case WM_PAINT:
  8. hDC = BeginPaint(hWnd, &ps);
  9. /* 创建双缓冲区 */
  10. // 创建与当前DC兼容的内存DC
  11. hDCMem = CreateCompatibleDC(hDC);
  12. // 创建一块指定大小的位图
  13. hBmpMem = CreateCompatibleBitmap(hDC, rect.right, rect.bottom);
  14. // 将该位图选入到内存DC中,默认是全黑色的
  15. hPreBmp = SelectObject(hDCMem, hMemBmp);
  16. /* 在双缓冲中绘图 */
  17. // 加载背景位图
  18. hBkBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
  19. hBrush = CreatePatternBrush(hBkBmp);
  20. GetClientRect(hWnd, &rect);
  21. FillRect(hDCMem, &rect, hBrush);
  22. DeleteObject(hBrush);
  23. /* 将双缓冲区图像复制到显示缓冲区 */
  24. BitBlt(hDC, 0, 0, rect.right, rect.bottom, hDCMem, 0, 0, SRCCOPY);
  25. /* 释放资源 */
  26. SelectObject(hDCMem, hPreBmp);
  27. DeleteObject(hMemBmp);
  28. DeleteDC(hDCMem);
  29. EndPaint(hWnd, &ps);
  30. break;
  31. }
  32. }

使用 Win32 版本时注意释放资源,释放顺序与创建顺序相反。我在使用过程中不小心遗漏了一句上面的 "DeleteObject(hMemBmp);"导致图形显示一段时间后就卡死了,查看内存使用发现内存随时间推移飙升,加上上面这句代码后,就没这个问题了。这也再次提醒我们释放资源是多么重要,成对编程的习惯是多么重要。
图 2  处理几次WM_PAINT消息后内存变化图
在使用过程中,如果想更新使用双缓冲区显示的区域,可以使用 InvalidateRect(hWnd, &rect, FALSE); ,这里要注意第三个参数一定要设置成 FALSE ,第三个参数表示更新第二个参数指定的区域时是否擦除背景,因为使用双缓冲技术时是直接复制整个缓冲区数据到显示缓冲区,因此无论原有缓冲区里面有什么都会被覆盖,因此第三个参数设置成  FALSE 有助于提高新能。更主要的原因是,如果先擦除原有缓冲区,会导致中间有一瞬间显示缓冲区被清空(显示为默认背景色),然后等到复制了双缓冲区的数据后再显示新的图像,这将导致闪烁!这与使用双缓冲的本意相违背,所以要注意这一点。
四、双缓冲使用 (C# 版本)
[csharp] view plaincopyprint?
  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. /* 像使用一般的 Graphics 一样绘图 */
  9. Pen pen = new Pen(Color.Gray);
  10. foreach (Step s in m_steps)
  11. {
  12. gc.DrawLine(pen, s.Start, s.End);
  13. }
  14. // 将双缓冲区中的图形渲染到指定画布上 (类似 Win32 中的)BitBlt
  15. backBuffer.Render(control.CreateGraphics());
  16. }
[csharp] view plaincopy print?
  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. /* 像使用一般的 Graphics 一样绘图 */
  9. Pen pen = new Pen(Color.Gray);
  10. foreach (Step s in m_steps)
  11. {
  12. gc.DrawLine(pen, s.Start, s.End);
  13. }
  14. // 将双缓冲区中的图形渲染到指定画布上 (类似 Win32 中的)BitBlt
  15. backBuffer.Render(control.CreateGraphics());
  16. }

其他版本后续用到时再补充。
参考资料:
文中用到的 Win32 API 在MSDN中的说明:
CreateCompatibleDC
CreateCompatibleBitmap 
BitBlt
C# 中使用double buffer 
http://msdn.microsoft.com/en-us/library/ms229622(v=vs.110).aspx

转载于:https://www.cnblogs.com/sky-heaven/p/6971938.html

双缓冲(Double Buffer)原理和使用【转】相关推荐

  1. 双缓冲技术绘图原理及简单的VC实现

    为了增加自己对双缓冲绘图技术的理解,简要做个笔记(以Windows为例): 1.Windows 绘图原理  我们在 Windows 环境下看到各种元素,如菜单.按钮.窗口.图像,从根本上说,都是&qu ...

  2. 基于VC6.0的控制台作图--动画和双缓冲技术

    文章目录 让图形动起来 直接向屏幕作图 使用双缓冲技术 双缓冲技术编程原理 让图形动起来 让图形动起来的办法就是不断定时清屏.重绘.像放电影那样.然而,直接向屏幕绘图是耗时的,在屏幕上画很多线,将多次 ...

  3. 【学习总结】MFC(CImagelist)(重绘)(双缓冲)

    双缓冲实现按钮列表 本文通过介绍双缓冲与CImageList,重写CButton类,来实现按钮列表. 简单的效果图 双缓冲实现 双缓冲的技术原理: 在Windows中每一种设备都在内存中有一个设备描述 ...

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

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

  5. Ping Pong Buffer 双缓冲 C++代码学习

    1.Ping Pong Buffer 原理分析 基本原理如上图所示,当设备有数据来时,先放入缓冲区1 然后将缓冲区1的数据放入缓冲区2,这时缓冲区1可接收下次数据.工作区可从缓冲区2拿数据 2.C++ ...

  6. mysql双写缓冲_MYSQL-写缓冲(change buffer)

    (1)MySQL数据存储包含内存与磁盘两个部分: (2)内存缓冲池(buffer pool)以页为单位,缓存最热的数据页(data page)与索引页(index page): (3)InnoDB以变 ...

  7. Qt的双缓冲技术(double buffering)

    Qt的双缓冲技术(double buffering)是Qt绘画机制的一部分,是一种在Qt4中被全面采用的技术.其核心是:把一个窗口部件渲染到一个脱屏pixmap(off- screen pixmap) ...

  8. 多线程异步日志系统,高效、强悍的实现方式-双缓冲

    作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 目录 文章目录 单片机中常用的环形缓冲区 多线程异步日志:双缓冲机制 双缓冲机制为什么高效 尽可能的降低 Lock 的时 ...

  9. STM32F407学习之DMA双缓冲模式HAL库实现

    本学期第一次培训要求用单片机实现1到40KHz的单步进变频正弦波输出,本菜鸡一开始用了通过修改定时器预分频系数和自动重装载值改变输出波表频率.修改定时器 + DDS两种方法,但实现的效果不太理想,不仅 ...

最新文章

  1. JS通过正则限制 input 输入框只能输入整数、小数(金额或者现金)
  2. python【蓝桥杯vip练习题库】ADV-234字符串跳步
  3. 使用FastJson解析时有关内部类的两个问题
  4. Message popover
  5. 在UltraEdit的查找和替换中使用正则表达式 (转)
  6. 云测试软件详解,软件测试之登录测试详解
  7. Python爬虫项目---批量下载深圳证券信息
  8. matlab学习笔记第七章——常微分方程(ODE)的数值解
  9. electron实践(2)
  10. 关于u8, u16, u32, u64
  11. paip.chrome使用ACTIVX 的总结
  12. 设备发现协议SSDP实现
  13. Linux驱动认知简明导论③ —— 修改树莓派引脚文件pin4Driver.c操作IO口
  14. jink remote server(远程调试)
  15. 树莓派外接显示器黑屏_树莓派4B连接显示器黑屏或者提示input not supported
  16. 支配树与Lengauer-Tarjan算法
  17. 乡愁 - 怀念老者余光中
  18. 基于Python实现的简易微信系统
  19. RTU、FTU、DTU、TTU都是什么鬼?
  20. 【DB笔试面试755】在Oracle的DG中,RFS、LNSn、MRP、LSP进程的作用分别是什么?

热门文章

  1. SQL SERVER 2008筛选时报错 无法为该请求检索数据
  2. 转 天才学生的天才回答
  3. 用 document.readyState == complete 判断页面是否加载完成。
  4. Mybatis Plus简介_代码_以及文档地址_以及前置知识---Mybatis Plus工作笔记001
  5. Spring Security OAuth2.0_实现分布式认证授权_搭建网关工程_Spring Security OAuth2.0认证授权---springcloud工作笔记151
  6. Sentinel服务熔断配置fallback和blockHandler_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0052
  7. Sentinel热点Key降级上_分布式系统集群限流_线程数隔离_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0042
  8. STM32工作笔记0025---理解并联电阻分流
  9. 关于bn层的进一步认识
  10. 杭电5253连接的管道