简体   繁体   English

在winforms应用程序中缓存GDI +对象:是否值得,如何正确执行?

[英]Caching GDI+ objects in a winforms application: is it worth it and how to do it right?

For some of my winforms applications I need to create a whole bunch of GDI+ objects (brushes, pens, fonts, etc) and use them over and over again. 对于我的一些winforms应用程序,我需要创建一大堆GDI +对象(画笔,笔,字体等)并一遍又一遍地使用它们。 I created a ghetto caching singleton to accomplish what I need, but the code smell is overwhelming... 我创建了一个贫民窟缓存单身人士来完成我需要的东西,但代码味道压倒性......

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];
    }
}
  1. Is there a better way for me to reuse these GDI+ objects, as opposed to instantiating them all over again every time OnPaint() etc gets called? 有没有更好的方法让我重用这些GDI +对象,而不是每次调用OnPaint()等时再次实例化它们?
  2. Will the GDI+ objects cause an unmanaged memory leak once the program terminates, or will the finalizer for each Brush object get called which will in turn release any unmanaged resources? 一旦程序终止,GDI +对象是否会导致非托管内存泄漏,或者是否会调用每个Brush对象的终结器,从而释放任何非托管资源?

I apologize if this is a repeat, but I didn't find any similar questions. 如果这是重复,我道歉,但我没有发现任何类似的问题。

There will not be memory leak but it's better to release GDI+ objects when it makes sense for you. 不会有内存泄漏但最好在你有意义时释放GDI +对象。 There are a limited amount of them in the operating system, so you might cause rendering issues in your and others applications. 操作系统中的数量有限,因此您可能会在您和其他应用程序中导致渲染问题。 Another thing to be mentioned is inability of GDI+ objects (fonts, etc.) to be used by 2+ threads the same time (some difficult to reproduce exceptions might be thrown). 另一件需要提及的是GDI +对象(字体等)无法同时被2个线程使用(可能会抛出一些难以重现的异常)。 You might be interested in some measurements of actual GDI+ objects creation time vs. possible exclusive locking delays. 您可能对实际GDI +对象创建时间与可能的独占锁定延迟的一些测量感兴趣。 "premature optimization is the root of all evil" © Donald Knuth “过早优化是所有邪恶的根源”©Donald Knuth

Actually it works for me to do some GDI+ objects caching: per painting cycle. 实际上它对我来说可以做一些GDI +对象缓存:每个绘画周期。 Client code might look like this: 客户端代码可能如下所示:

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
            ...
        }
    }
}

So we need GraphicsPalette to ignore nested construction and return the same brush for a given thread. 所以我们需要GraphicsPalette忽略嵌套构造并返回给定线程的相同画笔。 The suggested solution: 建议的解决方案:

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];
    }
}

Based on my experience with VG.net, I do not believe caching GDI+ objects is usually worth the trouble, except for large things like Bitmaps. 根据我对VG.net的经验,我不相信缓存GDI +对象通常是值得的,除了像Bitmaps这样的大型事情。 Of course it is easy to measure. 当然,它很容易衡量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM