- IntPtrhandle=font.ToHfont();//性能瓶颈
- //…
- 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做特别处理,保证所有关键终止代码都有机会执行。
- Code
- #regionTheSafeFontHandleclass
- internalsealedclassSafeFontHandle:SafeHandleZeroOrMinusOneIsInvalid
- {
- privateSafeFontHandle()
- :base(true)
- {
- }
- publicSafeFontHandle(IntPtrpreexistingHandle,boolownsHandle)
- :base(ownsHandle)
- {
- base.SetHandle(preexistingHandle);
- }
- protectedoverrideboolReleaseHandle()
- {
- returnSafeNativeMethods.DeleteNativeFontHandle(base.handle);
- }
- }
- #endregion
2,使用HandleCollector类防止Font的Handle超过操作系统***限制。HandleCollector会跟踪Font的Handle,并在其达到指定阀值时强制执行垃圾回收。垃圾回收后,SafeFontHandle会释放Font的handle。
- Code
- [SuppressUnmanagedCodeSecurity]
- internalstaticclassSafeNativeMethods
- {
- privatestaticHandleCollectorFontHandleCollector=newHandleCollector("GdiFontHandle",500,1000);
- internalstaticIntPtrCreateNativeFontHandle(Fontfont)
- {
- IntPtrhandle=font.ToHfont();
- if(handle!=IntPtr.Zero)
- {
- FontHandleCollector.Add();
- }
- returnhandle;
- }
- internalstaticboolDeleteNativeFontHandle(IntPtrhandle)
- {
- boolsuccess=DeleteObject(handle);
- if(success)
- {
- FontHandleCollector.Remove();
- }
- returnsuccess;
- }
- [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
- internalstaticexternboolDeleteObject(System.IntPtrgdiObject);
- }
3,使用弱引用缓存类WeakReferenceCachePool
- Code
- internalstaticclassSafeFontHandleFactory
- {
- #regionInstanceData
- privatestaticWeakReferenceCachePool_cachePool=newWeakReferenceCachePool();
- #endregion
- #regionMethods
- publicstaticSafeFontHandleCreateSafeFontHandle(Fontfont)
- {
- if(font==null)
- {
- thrownewArgumentNullException();
- }
- SafeFontHandlesafeFontHandle=_cachePool[font];
- if(safeFontHandle==null)
- {
- IntPtrnativeHandle=SafeNativeMethods.CreateNativeFontHandle(font);
- safeFontHandle=newSafeFontHandle(nativeHandle,true);
- _cachePool[font]=safeFontHandle;
- }
- returnsafeFontHandle;
- }
- #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
【编辑推荐】