分析提高GDI编程性能的方法

开发 后端
提高GDI编程性能一般都是从性能瓶颈开始。项目中有这样一个控件,它包含很多个Item,每个Item字体可能相同,也可能不同。且该控件经常在同一个Form上大量使用。正是这个控件在使用GDI画每个Item的文字时,出现了性能瓶颈。
  1. IntPtrhandle=font.ToHfont();//性能瓶颈  
  2. //…  
  3. SafeNativeMethods.DeleteObject(handle); 

由于该控件在使用GDI画字时,通过调用Font.ToHfont()方法获得Font的Handle。而这个方法非常慢。并且控件在画每个Item时都被调用这个方法,Form中又有很多个这样的控件,因此调用次数相当可观。这就造成了这个性能瓶颈。

由于操作系统是不允许GDI的Handle个数大于9999的。如果大于9999个的话,程序就会崩掉。因此,我们绝对不能使程序中GDI的Handle个数与某些因素有线性增长关系。所有,一般都是在使用GDI画字时创建Handle,用完之后就删除掉。这样也可以防止GDI泄露。

考虑到很多时候,Font都是相同的,如果能将Font创建的Handle缓存起来,性能就会有很大的提升。但是,缓存的Handle不及时删除的话,如果Font不相同的太多,就有机会达到操作系统允许的***个数,从而使程序崩溃。

以下是我的提高GDI编程性能解决方案:

1,使用SafeFontHandle类来防止GDI泄露。SafeFontHandle派生自SafeHandleZeroOrMinusOneIsInvalid,而SafeHandleZeroOrMinusOneIsInvalid又派生自CriticalFinalizerObject。GC会对CriticalFinalizerObject做特别处理,保证所有关键终止代码都有机会执行。

  1. Code  
  2. #regionTheSafeFontHandleclass  
  3.  
  4. internalsealedclassSafeFontHandle:SafeHandleZeroOrMinusOneIsInvalid  
  5. {  
  6. privateSafeFontHandle()  
  7. :base(true)  
  8. {  
  9. }  
  10.  
  11. publicSafeFontHandle(IntPtrpreexistingHandle,boolownsHandle)  
  12. :base(ownsHandle)  
  13. {  
  14. base.SetHandle(preexistingHandle);  
  15. }  
  16.  
  17. protectedoverrideboolReleaseHandle()  
  18. {  
  19. returnSafeNativeMethods.DeleteNativeFontHandle(base.handle);  
  20. }  
  21. }  
  22. #endregion 

2,使用HandleCollector类防止Font的Handle超过操作系统***限制。HandleCollector会跟踪Font的Handle,并在其达到指定阀值时强制执行垃圾回收。垃圾回收后,SafeFontHandle会释放Font的handle。

  1. Code  
  2. [SuppressUnmanagedCodeSecurity]  
  3. internalstaticclassSafeNativeMethods  
  4. {  
  5. privatestaticHandleCollectorFontHandleCollector=newHandleCollector("GdiFontHandle",500,1000);  
  6.  
  7. internalstaticIntPtrCreateNativeFontHandle(Fontfont)  
  8. {  
  9. IntPtrhandle=font.ToHfont();  
  10. if(handle!=IntPtr.Zero)  
  11. {  
  12. FontHandleCollector.Add();  
  13. }  
  14. returnhandle;  
  15. }  
  16.  
  17. internalstaticboolDeleteNativeFontHandle(IntPtrhandle)  
  18. {  
  19. boolsuccess=DeleteObject(handle);  
  20. if(success)  
  21. {  
  22. FontHandleCollector.Remove();  
  23. }  
  24. returnsuccess;  
  25. }  
  26.  
  27. [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]  
  28. internalstaticexternboolDeleteObject(System.IntPtrgdiObject);  

3,使用弱引用缓存类WeakReferenceCachePool来缓存SafeFontHandle,这样可以不影响SafeFontHandle被GC正常垃圾回收,从而释放Font的Handle。关于弱引用缓存类WeakReferenceCachePool,可以参考《一个弱引用缓存类》这篇文章。

  1. Code  
  2. internalstaticclassSafeFontHandleFactory  
  3. {  
  4. #regionInstanceData  
  5. privatestaticWeakReferenceCachePool_cachePool=newWeakReferenceCachePool();  
  6. #endregion  
  7. #regionMethods  
  8. publicstaticSafeFontHandleCreateSafeFontHandle(Fontfont)  
  9. {  
  10. if(font==null)  
  11. {  
  12. thrownewArgumentNullException();  
  13. }  
  14. SafeFontHandlesafeFontHandle=_cachePool[font];  
  15. if(safeFontHandle==null)  
  16. {  
  17. IntPtrnativeHandle=SafeNativeMethods.CreateNativeFontHandle(font);  
  18. safeFontHandle=newSafeFontHandle(nativeHandle,true);  
  19. _cachePool[font]=safeFontHandle;  
  20. }  
  21. returnsafeFontHandle;  
  22. }  
  23. #endregion  

这样就成功的缓存了GDI的Handle,而且在使用完成后,GDI的Handle不会线性增长,只要有GC回收发生,GDI的Handle都会清零,或者总个数达到HandleCollector指定的阀值时,也会清零。利用GC垃圾回收机制,在性能和内存占用之间自动平衡。

这里是测试代码,提高GDI编程性能测试如下:

不使用弱引用缓存

TimeElapsed:350ms
CPUCycles:952,061,115
Gen0:1
Gen1:0
Gen2:0
GDIincrement:0

提高GDI编程性能,使用弱引用缓存

TimeElapsed:42ms
CPUCycles:142,020,499
Gen0:0
Gen1:0
Gen2:0
GDIincrement:0

【编辑推荐】

.NET图像编程(GDI+)自制“小屏保”

C# GDI+编程之基础篇

VC++.NET的GDI+入门之位图和图标

C#中使用GDI 让网站新闻标题个性化

VC中使用Gdi+合并jpg图片

责任编辑:彭凡 来源: 博客园
相关推荐

2011-05-24 15:15:12

mysql性能

2010-01-05 18:49:57

.NET Framew

2024-10-25 15:48:21

GPUPyTorch编程

2015-03-18 09:59:14

CSSCSS提高渲染性

2011-07-15 10:27:21

PHP

2020-09-23 09:20:58

代码Java字符串

2009-07-20 17:07:30

提高ASP.NET性能

2012-05-15 09:46:37

监听

2009-09-18 09:16:06

.NET垃圾回收

2017-09-05 17:16:18

多维数据分析

2023-05-17 16:47:47

物联网智能建筑

2021-04-19 10:08:48

优化CSS性能

2010-10-27 15:26:42

Oracle执行计划

2024-04-24 08:25:00

性能Android生命周期

2009-10-16 10:36:35

VB.NET绘制GDI

2010-05-07 08:59:50

ASP.NET

2012-11-27 10:21:25

无线技术WLANAP

2021-11-25 07:01:57

.NET开发编程

2021-06-02 05:47:32

程序员编程开发

2009-05-06 09:56:22

Ruby性能改进
点赞
收藏

51CTO技术栈公众号