最近在写一个程序涉及列表控件的大量数据刷新,使用了ListView控件,但觉得逐条向ListView添加数据的方式性能不是很高,想改为数据绑定方式,网上搜索半天发现ListView不支持数据绑定,作罢,索性测试下ListView和DataGridView的性能。
性能测试静态类(负责测试耗时和内存占用)
public static class PerformanceMonitor
{
private static Stopwatch sw = new Stopwatch();
private static float duration = 0;
public static void StopwatchStart()
{
sw.Start();
}
public static void StopwatchStop()
{
sw.Stop();
duration = (float)sw.Elapsed.TotalSeconds;
}
public static void PrintStopwatchDuration()
{
Console.WriteLine(string.Format("耗时:{0}s", duration.ToString()));
}
public static double GetProcessUsedMemory()
{
double usedMemory = 0;
usedMemory = Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0;
Console.WriteLine(string.Format("内存占用:{0}M", usedMemory.ToString()));
return usedMemory;
}
}
消息产生类(负责产生指定个数的消息)
public class Generator
{
private Thread generateMessageThread;//消息产生线程
private int count;//消息数目
public delegate void GenerateOneMessageEventHandler(string message);
public event GenerateOneMessageEventHandler GenerateOneMessageEvent;
public Generator()
{
count = 0;
}
public void StartGenerate(int num)
{
//产生num条消息
count = num;
generateMessageThread = new Thread(new ThreadStart(GenerateMessage));
generateMessageThread.IsBackground = true;
generateMessageThread.Start();
}
private void GenerateMessage()
{
PerformanceMonitor.StopwatchStart();//开始计时
string message = string.Empty;
for (int i = 0; i < count; i++)
{
message = "One Message";
if (GenerateOneMessageEvent != null)
{
GenerateOneMessageEvent(message);
}
}
PerformanceMonitor.StopwatchStop();//结束计时
PerformanceMonitor.PrintStopwatchDuration();//打印耗时
PerformanceMonitor.GetProcessUsedMemory();//打印内存占用
}
}
ListView控件逐条添加方式
Form类:
public partial class FormListView : Form
{
private int sequenceNumber;//列表序号
private ListViewItem lvi;//列表项
Generator messageGenerator1;//消息产生类,由事件触发列表的更新
private delegate void AppendMessageDelegate(string message);
public FormListView()
{
InitializeComponent();
listView.Columns.Add("序号");
listView.Columns.Add("消息");
sequenceNumber = 0;
messageGenerator1 = new Generator();
messageGenerator1.GenerateOneMessageEvent += new Generator.GenerateOneMessageEventHandler(AppendOneMessageToListView);
messageGenerator1.StartGenerate(1000);
}
public void AppendOneMessageToListView(string message)
{
if (listView.InvokeRequired)
{
AppendMessageDelegate del = new AppendMessageDelegate(AppendOneMessageToListView);
listView.Invoke(del, message);
return;
}
sequenceNumber++;
lvi = new ListViewItem(sequenceNumber.ToString());
lvi.SubItems.Add(message);
listView.Items.Add(lvi);
}
}
5次测试结果:
|
10条 |
100条 |
1000条 |
10000条 |
100000条 |
耗时(S) |
< 0.1 |
< 0.5 |
3 |
30 |
380 |
内存占用(M) |
23.04296875 |
23.43359375 |
24.52734375 |
29.71875 |
79.73828125 |
DataGridView逐条添加方式
Form类:
public partial class FormDataGridView : Form
{
private int sequenceNumber;//列表序号
Generator messageGenerator2;//消息产生类,由事件触发列表的更新
private delegate void AppendMessageDelegate(string message);
public FormDataGridView()
{
InitializeComponent();
dataGridView.Columns.Add("序号", "序号");
dataGridView.Columns.Add("消息", "消息");
messageGenerator2 = new Generator();
messageGenerator2.GenerateOneMessageEvent += new Generator.GenerateOneMessageEventHandler(AppendOneMessageToDataGridView);
messageGenerator2.StartGenerate(1000);
}
public void AppendOneMessageToDataGridView(string message)
{
if (dataGridView.InvokeRequired)
{
AppendMessageDelegate del = new AppendMessageDelegate(AppendOneMessageToDataGridView);
dataGridView.Invoke(del, message);
return;
}
sequenceNumber++;
dataGridView.Rows.Add(new object[] { sequenceNumber, message });
}
}
5次测试结果:
|
10条 |
100条 |
1000条 |
10000条 |
100000条 |
耗时(S) |
< 0.1 |
< 0.3 |
1.3 |
17 |
550 |
内存占用(M) |
23.6328125 |
24.3203125 |
24.91796875 |
30.140625 |
65.84375 |
DataGridView数据绑定方式(只负责绑定)
Form类:
public partial class FormDataGridView : Form
{
private int sequenceNumber;//列表序号
private DataTable messageTable;
Generator messageGenerator3;
public FormDataGridView()
{
InitializeComponent();
messageGenerator3 = new Generator3();
messageGenerator3.GenerateOneMessageEvent += new Generator.GenerateOneMessageEventHandler(AppendOneMessageToDataTable);
messageTable = new DataTable();
messageTable.Columns.Add("序号");
messageTable.Columns.Add("消息");
dataGridView.DataSource = messageTable;
messageGenerator3.StartGenerate(1000);
}
private void AppendOneMessageToDataTable(string message)
{
sequenceNumber++;
messageTable.Rows.Add(new object[] { sequenceNumber, message });
}
}
5次测试结果:
|
10条 |
100条 |
1000条 |
10000条 |
100000条 |
耗时(S) |
< 0.01 |
0.7 |
5 |
56 |
… |
内存占用(M) |
23.3359375 |
25.87890625 |
26.359375 |
32.3671875 |
… |
DataGridView数据绑定方式(定量/定时重绑定)
Form类:
public partial class FormDataGridView : Form
{
private int sequenceNumber;//列表序号
private DataTable messageTable;
private DataTable messageTableBuffer;
Generator messageGenerator4;
private delegate void ChangeDataSourceDelegate();
public FormDataGridView()
{
InitializeComponent();
messageGenerator4 = new Generator3();
messageGenerator4.GenerateOneMessageEvent += new Generator.GenerateOneMessageEventHandler(AppendOneMessageToDataTable);
messageTable = new DataTable();
messageTableBuffer = new DataTable();
messageTable.Columns.Add("序号");
messageTable.Columns.Add("消息");
messageGenerator3.StartGenerate(10000);
}
private void AppendOneMessageToDataTable(string message)
{
sequenceNumber++;
lock (messageTable)
{
messageTable.Rows.Add(new object[] { sequenceNumber, message });
if (sequenceNumber % 1000 == 0)
{
//1000条数据重绑定
ChangeDataSource();
}
}
}
public void ChangeDataSource()
{
if (dataGridView.InvokeRequired)
{
ChangeDataSourceDelegate del = new ChangeDataSourceDelegate(ChangeDataSource);
dataGridView.Invoke(del);
return;
}
dataGridView.DataSource = null;//解绑定
messageTableBuffer.Merge(messageTable.Copy());//将messageTable数据添加至messageTableBuffer
messageTable.Clear();//清除messageTable
dataGridView.DataSource = messageTableBuffer;//重绑定
}
}
3次测试结果:
|
1000条 |
10000条 |
100000条 |
耗时(S) |
< 0.1 |
0.6 |
16 |
内存占用(M) |
24.5078125 |
33.1484375 |
73.05859375 |
界面对比
总结
1、 直接向列表添加数据和绑定数据源两种方式的内存占用差距并不大;
2、 以直接向列表添加数据的方式,在数据量不大的情况下DataGridView占优,在大量数据时ListView的添加速度更快;但在实际使用中并不会频繁进行数据的添加,应该说这两种方式的差异不是很大;
3、 在使用DataGridView进行数据绑定后,若频繁地更新数据源,将会严重影响性能,原因应该是每次数据更新都会导致DataGridView发生重绘而降低性能,而当数据更新速度非常快时,甚至可能导致UI线程来不及绘制而发生错误;
4、 DataGridView的数据绑定适合大量数据的显示,而且这种方式也方便对数据进行修改,但不适合数据的频繁更新,故在数据频繁更新情况下,可采用重新绑定的方式,如上例4所示,首先开辟一个缓冲表,在一定量数据更新或者一定时间后重新绑定数据源,这样在性能上得到了明显的提升,远远好于其它三种方式,但缺点是牺牲了一定的内存空间;
5、 对于功能方面来说,DataGridView比ListView强大,不仅仅可以绑定各种数据源,还具有很多高级的功能,对于上面那种数据频繁更新的情况,可能有更好的解决方案,另外在界面方面,感觉ListView更美观些;