Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 缓存GDI+;winforms应用程序中的对象:值得吗?如何正确操作?_C#_.net_Winforms_Gdi+_System.drawing - Fatal编程技术网

C# 缓存GDI+;winforms应用程序中的对象:值得吗?如何正确操作?

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

对于我的一些winforms应用程序,我需要创建一大堆GDI+对象(画笔、笔、字体等),并反复使用它们。我创建了一个贫民区缓存单身汉来完成我所需要的,但代码的味道是压倒性的

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[颜色];
}
}
  • 有没有更好的方法来重用这些GDI+对象,而不是每次调用
    OnPaint()
    etc时都重新实例化它们
  • 一旦程序终止,GDI+对象是否会导致非托管内存泄漏,或者是否会调用每个
    对象的终结器,从而释放任何非托管资源

  • 如果这是重复,我很抱歉,但我没有发现任何类似的问题。

    不会出现内存泄漏,但最好在对您有意义时释放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方法并除去对象。它绝对值得重用一个或多个位图,而不是将它们扔掉。