C# 缓存GDI+;winforms应用程序中的对象:值得吗?如何正确操作?
对于我的一些winforms应用程序,我需要创建一大堆GDI+对象(画笔、笔、字体等),并反复使用它们。我创建了一个贫民区缓存单身汉来完成我所需要的,但代码的味道是压倒性的C# 缓存GDI+;winforms应用程序中的对象:值得吗?如何正确操作?,c#,.net,winforms,gdi+,system.drawing,C#,.net,Winforms,Gdi+,System.drawing,对于我的一些winforms应用程序,我需要创建一大堆GDI+对象(画笔、笔、字体等),并反复使用它们。我创建了一个贫民区缓存单身汉来完成我所需要的,但代码的味道是压倒性的 public sealed class GraphicsPalette { public static readonly GraphicsPalette Instance = new GraphicsPalette(); static GraphicsPalette() { } p
public sealed class GraphicsPalette
{
public static readonly GraphicsPalette Instance = new GraphicsPalette();
static GraphicsPalette()
{
}
private Dictionary<Color, Brush> solidBrushes;
//multithreading
private object brushLock;
private GraphicsPalette()
{
solidBrushes = new Dictionary<Color, Brush>();
brushLock = new object();
}
public Brush GetSolidBrush(Color color, int alpha)
{
return GetSolidBrush(Color.FromArgb(alpha, color));
}
public Brush GetSolidBrush(Color color)
{
if (!solidBrushes.ContainsKey(color))
{
lock (brushLock)
{
if (!solidBrushes.ContainsKey(color))
{
Brush brush = new SolidBrush(color);
solidBrushes.Add(color, brush);
return brush;
}
}
}
return solidBrushes[color];
}
}
公共密封类GraphicsPalette
{
公共静态只读GraphicsPalette实例=新GraphicsPalette();
静态图形spalette()
{
}
私人词典;
//多线程
私有对象刷锁;
私有图形spalette()
{
solidBrush=新字典();
brushLock=新对象();
}
公共画笔GetSolidBrush(颜色,int alpha)
{
返回GetSolidBrush(Color.FromArgb(alpha,Color));
}
公共画笔GetSolidBrush(彩色)
{
如果(!solidBrush.ContainsKey(颜色))
{
锁(刷锁)
{
如果(!solidBrush.ContainsKey(颜色))
{
笔刷=新的SolidBrush(颜色);
添加(颜色、笔刷);
回刷;
}
}
}
返回SolidBrush[颜色];
}
}
OnPaint()
etc时都重新实例化它们刷
对象的终结器,从而释放任何非托管资源如果这是重复,我很抱歉,但我没有发现任何类似的问题。不会出现内存泄漏,但最好在对您有意义时释放GDI+对象。操作系统中的渲染数量有限,因此可能会导致您和其他应用程序出现渲染问题。另一件需要提及的事情是GDI+对象(字体等)无法被2+线程同时使用(可能会抛出一些难以重现的异常)。您可能对实际GDI+对象创建时间与可能的独占锁定延迟的一些度量感兴趣。“过早优化是万恶之源”©Donald Knuth 实际上,对我来说,做一些GDI+对象缓存是可行的:每个绘制周期。客户端代码可能如下所示:
class Visual
{
public void Draw()
{
using (new GraphicsPalette()) {
DrawHeader();
DrawFooter();
}
}
private void DrawHeader() {
var brush = GraphicsPalette.GetSolidBrush(Color.Green);
...
}
public void DrawFooter() {
using (new GraphicsPalette()) { // ensures palette existence; does nothing if there is a palette on the stack
var brush = GraphicsPalette.GetSolidBrush(Color.Green); // returns the same brush as in DrawHeader
...
}
}
}
因此,我们需要GraphicsPalette忽略嵌套构造,并为给定线程返回相同的画笔。建议的解决办法是:
public class GraphicsPalette : IDisposable
{
[ThreadStatic]
private static GraphicsPalette _current = null;
private readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>();
public GraphicsPalette()
{
if (_current == null)
_current = this;
}
public void Dispose()
{
if (_current == this)
_current = null;
foreach (var solidBrush in _solidBrushes.Values)
solidBrush.Dispose();
}
public static SolidBrush GetSolidBrush(Color color)
{
if (!_current._solidBrushes.ContainsKey(color))
_current._solidBrushes[color] = new SolidBrush(color);
return _current._solidBrushes[color];
}
}
公共类GraphicsPalette:IDisposable
{
[线程静态]
私有静态GraphicsPalette _current=null;
专用只读词典_solidBrush=新词典();
公共图形spalette()
{
如果(_current==null)
_电流=这个;
}
公共空间处置()
{
如果(_current==此)
_电流=零;
foreach(var solidBrush in_solidBrush.Values)
solidBrush.Dispose();
}
公共静态SolidBrush GetSolidBrush(彩色)
{
如果(!\u当前.\u solidBrush.ContainsKey(颜色))
_当前。\u SolidBrush[颜色]=新的SolidBrush(颜色);
返回_current._solidBrush[颜色];
}
}
根据我使用VG.net的经验,我认为缓存GDI+对象通常不值得这么麻烦,除了位图之类的大型对象。当然,这很容易衡量。通过调整单例,您可以轻松测试任何性能提升。添加一个标志,使其在单例和…“多例”之间切换(我编造了这个词)。multi-ton将始终删除并重新创建资源。然后在这里报告。可能的副本不是真的。我想知道的是,如果我挂在一堆GDI+对象(画笔)上,并且没有显式地对它们调用Dispose(),我是否可以保证当我的应用程序域终止时,它们都会被正确地最终确定并自动处理?如果不是这样的话,那么我要看的是一个非托管内存泄漏。由Microsoft实现和建议的dispose模式要求您实现一个dispose(bool)
方法,该方法通过dispose()
方法作为参数传递false
,并从终结器调用,作为参数传递true
。所以我认为关于内存泄漏,你是安全的。但是我会特别注意创建太多的临时资源,因为这可能会导致内存紧张(它们可能会在某个时候被处理掉,但不能保证GC会按时到达它们…),如果您一直在使用这些资源,请不要随意处理它们。如果您只是偶尔使用一次,请立即创建/处置它们(通常是建议)。它都是被管理的,所以框架会注意在应用程序生命周期结束时处理所有内容。在应用程序运行时,您应该主要担心内存泄漏。回答得好。实际上,我考虑在winforms控件中缓存GDI对象,而不是单例。这样,当控件不再需要或某个特定窗体关闭时,我可以连接到控件的dispose方法并除去对象。它绝对值得重用一个或多个位图,而不是将它们扔掉。