C# GDI的最佳战略+;对象生存期?

C# GDI的最佳战略+;对象生存期?,c#,winforms,gdi+,idisposable,C#,Winforms,Gdi+,Idisposable,在我们的应用程序中,我们有一些GDI+对象,它们经常在许多不同的上下文中使用。这包括Font,SolidBrush(白色,黑色…)的一些实例,以及笔的一些实例 对于这些对象,到目前为止,我们的策略是通过广泛可见的静态只读字段保存它们。这避免了数百万次创建/处置它们。当然,我们负责对这些对象的线程安全访问(它们基本上是从UI线程访问的) 在应用程序的生命周期中,只有少数GDI+对象可以保存,比如说200个。其他GDI+对象(寿命较短的对象)都将尽快处理。但我们有时会遇到意外的GDI+资源中断异常,

在我们的应用程序中,我们有一些GDI+对象,它们经常在许多不同的上下文中使用。这包括
Font
SolidBrush
(白色,黑色…)的一些实例,以及
笔的一些实例

对于这些对象,到目前为止,我们的策略是通过广泛可见的静态只读字段保存它们。这避免了数百万次创建/处置它们。当然,我们负责对这些对象的线程安全访问(它们基本上是从UI线程访问的)

在应用程序的生命周期中,只有少数GDI+对象可以保存,比如说200个。其他GDI+对象(寿命较短的对象)都将尽快处理。但我们有时会遇到意外的GDI+资源中断异常,希望很少发生


我想知道这些异常是否来自于这几个GDI+对象,而这是否是一个更明智的策略来创建/处理大量寿命较短的GDI+对象。有人对这两种策略有实际经验和相关结论吗?

缓存系统。绘制对象是错误的。它们的制造成本很低,在周围的环境中很昂贵。它们被分配到一个特殊的堆上,桌面上的所有进程都需要共享。堆大小限制为65535个对象。创建画笔或笔之类的对象大约需要一微秒的时间,与使用它们执行操作的成本相比,这是微乎其微的。唯一昂贵的绘图对象是字体。创建一个需要字体映射器承担相当大的开销。但是.NET已经解决了这个问题,它缓存了它们

除了不必要地占用堆,编程风格是危险的。很容易忘记盲目应用using语句。这肯定会给您带来麻烦,您需要依赖终结器线程再次释放它们。如果一个程序涉及大量繁重的绘制,但没有足够的对象分配来触发GC,那么您将耗尽GDI对象的配额。默认情况下,一个进程有10000个对象。这将使你的程序崩溃。请注意,System.Drawing类包装器的大小不足以单独触发GC,其中10000个不足以让终结器运行并再次释放句柄


问题很容易诊断,您可以使用任务管理器。使用查看+选择列并勾选“GDI对象”。不妨勾选“用户对象”,另一个很容易泄露的对象。在使用过程中,请注意显示的进程计数。一个不断攀升的数字意味着厄运。

有没有可能你持有的GDI+对象比你想象的要多,并且错误是由太多的GDI+句柄造成的?另外,你可以尝试使用.NET提供的静态对象,而不是持有自己的静态对象,就像是一些关于这个问题的链接。听起来你真的有资源泄漏的地方。是的,您一次可以拥有多少GDI+资源是有限制的,但数量至少应该是上万。不,我们没有泄漏,请参阅下面我对Hans的回答。Hans,感谢您提供的详细回答,看起来您自己编写了Windows窗体和GDI+代码,不是吗?;)我将应用你的建议,并在这里报告我的经验。关于#Handles,到目前为止,我已经对它进行了多年的监控,即使在对象少于1000个的进程上,中断资源异常仍然会发生。希望这些异常非常罕见(我知道,因为我们有一个生产异常远程记录器)。泄漏它们是可能的,但非常罕见;缓存对象通常是一个错误。如果您只缓存了几个对象,这并不总是一个错误。另外,在渲染时缓存一些对象并在渲染完成后释放它们也没有什么害处。我实际上正在缓存GDI+对象。到目前为止,它工作得很好,只是我遇到了一个不可行的情况:当处理数千个需要各种字体样式的TreeNode时(有些粗体,有些下划线…),因此TreeNode.NodeFont引用缓存的字体对象。有没有人想到另一种方法?当然TreeNode可以用短寿命字体自定义绘制,但这听起来让我难以接受,不是吗?!缓存系统。绘制对象是一个错误