在我正在开发的应用程序中,我们使用DevExpress XtraGrid控件,该控件具有RowCellStyle
事件,允许自定义每个单元格的样式。此事件的事件处理程序通常如下所示:
private gridView1_RowCellStyle(object sender, RowCellStyleEventArgs e)
{
if (/* Some condition */)
{
e.Appearance.Font = new Font(gridView1.Appearance.Font, FontStyle.Bold);
}
}
每次渲染单元格时都会调用此处理程序,因此它可以创建大量Font
个实例。所以我想知道这样做的成本......我做了一些实验,似乎每次都会创建一个新的HFONT手柄。我应该担心吗?对资源使用的影响有多大?
如果它对性能产生重大影响,是否应该有FontCache
类或类似的东西?
注意:我知道如何解决问题(我只需要创建一次字体并每次都重复使用),我的问题是关于创建许多HFONT句柄的成本
答案 0 :(得分:6)
测试;我通过重用获得了双重性能(在发布,重用= 3000毫秒,重新创建= 4900毫秒)
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;
static class Program
{
static void Main()
{
Button btn1, btn2;
Form form = new Form
{
Controls = {
(btn1 = new Button { Dock = DockStyle.Bottom, Text = "reuse" }),
(btn2 = new Button { Dock = DockStyle.Bottom, Text = "recreate"})
}
};
btn1.Click += delegate
{
var watch = Stopwatch.StartNew();
using (var gfx = form.CreateGraphics())
using (var font = new Font(SystemFonts.DefaultFont, FontStyle.Bold))
{
gfx.Clear(SystemColors.Control);
for (int i = 0; i < 10000; i++)
{
gfx.DrawString("abc", font, SystemBrushes.ControlText, i % 103, i % 152);
}
}
watch.Stop();
form.Text = watch.ElapsedMilliseconds + "ms";
};
btn2.Click += delegate
{
var watch = Stopwatch.StartNew();
using (var gfx = form.CreateGraphics())
{
gfx.Clear(SystemColors.Control);
for (int i = 0; i < 10000; i++)
{
using (var font = new Font(SystemFonts.DefaultFont, FontStyle.Bold))
{
gfx.DrawString("abc", font, SystemBrushes.ControlText, i % 103, i % 152);
}
}
}
watch.Stop();
form.Text = watch.ElapsedMilliseconds + "ms";
};
Application.Run(form);
}
}
答案 1 :(得分:2)
Font实现了IDisposable - 你应该确保在完成后调用Dispose。
它是一种非托管资源,因此操作系统最终可能会耗尽资源以使用更多对象来刺激您。
检查TaskManager中的GDI句柄计数,看它是否持续增加。
答案 2 :(得分:2)
重用几乎总是比按需更快,但重用或缓存Font对象的能力可能会有所不同。
如果要重复使用Font对象并将不同的Font对象与每个控件相关联,则会增加超出gdi句柄限制的机会。这将损害给定用户在发生时使用的所有应用程序。
理想情况下,您应该在应用程序/进程级别缓存Font对象,并且仅缓存必要的最小集合(最多可能是十几个)。此外,缓存一小组共享字体和其他GDI对象可以让您更好地处理棘手的消息,如WM_SETTINGCHANGE。在您的示例中,您正在修改默认值,并且当用户更改方案或Windows中的默认显示字体时,您可能会遇到绘图错误。在WM_SETTINGCHANGE期间,您会考虑发布缓存副本并初始化或准备新的字体对象集。
WM_SETTINGCHANGE (Windows) @ MSDN
如果您需要提供数百种不同的字体(或其他gdi对象),则不应缓存字体,只能根据需要创建所需内容。这不是性能友好的,但它将帮助您的应用程序与用户可能打开的其他应用程序共存。持有数百个GDI对象将极大地增加用户遇到GDI句柄限制的可能性。但是,基于引用计数的缓存可以提供最适合您的权衡,只保留最常用变体的硬拷贝。
最后,正如Matt Breckon所提到的,对所有可用的GDI对象使用IDisposable。这将帮助您避免gdi句柄泄漏。