处理非常大的数据集&及时加载

时间:2011-01-26 16:27:16

标签: c# file-io datagridview large-data

我有一个用C#(.NET 4.0)编写的.NET应用程序。在此应用程序中,我们必须从文件中读取大型数据集,并以类似网格的结构显示内容。因此,为了实现这一点,我在表单上放置了一个DataGridView。它有3列,所有列数据都来自文件。最初,该文件有大约600.000条记录,对应于DataGridView中的600.000行。

我很快发现,DataGridView崩溃了这么大的数据集,所以我切换到虚拟模式。为了实现这一点,我首先将文件完全读入3个不同的数组(对应3列),然后触发CellValueNeeded事件,我从数组中提供正确的值。

然而,正如我们很快发现的那样,这个文件中可能有一个巨大的(巨大的!)记录数。当记录大小非常大时,将所有数据读入阵列或List<>等似乎是不可行的。我们很快就会遇到内存分配错误。 (内存不足)。

我们卡在那里,但后来意识到,为什么首先将数据全部读入数组,为什么不在CellValueNeeded事件触发时按需读取文件?这就是我们现在所做的:我们打开文件,但不读取任何内容,并且当CellValueNeeded事件触发时,我们首先将Seek()放到文件中的正确位置,然后读取相应的数据。

这是我们能想到的最好的,但是,首先这是非常缓慢的,这使得应用程序缓慢且用户不友好。其次,我们不禁认为必须有更好的方法来实现这一目标。例如,某些二进制编辑器(如HXD)对于任何文件大小来说都非常快,所以我想知道如何实现这一点。

哦,并且为了解决我们的问题,在DataGridView的虚拟模式中,当我们将RowCount设置为文件中的可用行数(比如16.000.000)时,DataGridView甚至需要一段时间来初始化本身。对此“问题”的任何评论也将受到赞赏。

由于

5 个答案:

答案 0 :(得分:5)

如果您无法将整个数据集放入内存中,那么您需要一个缓冲方案。您的应用程序应该预测用户的操作并提前预读,而不是只读取填充DataGridView以响应CellValueNeeded所需的数据量。因此,例如,当程序首次启动时,它应该读取前10,000条记录(或者可能只读取1,000条或者大约100,000条 - 在您的情况下是合理的)。然后,CellValueNeeded请求可以立即从内存中填充。

当用户在网格中移动时,您的程序尽可能地比用户领先一步。如果用户跳到你前面可能会有短暂停顿(比如说,想要从前面跳到最后),你必须转到磁盘才能完成请求。

缓冲通常最好由一个单独的线程完成,虽然如果线程在预期用户的下一个操作时正在读取,同时有时会出现同步问题,然后用户会做一些完全出乎意料的事情,例如跳转到开头的列表。

除非记录非常大,否则1600万条记录并非真正记录在内存中的所有记录。或者,如果您的服务器上没有太多内存。当然,除非List<T>是值类型(结构),否则1600万不会接近T的最大大小。你在这里谈论了多少千兆字节的数据?

答案 1 :(得分:3)

嗯,这是一个似乎更好的解决方案:

步骤0:将dataGridView.RowCount设置为较低的值,例如25(或适合表单/屏幕的实际数字)

步骤1:禁用dataGridView的滚动条。

第2步:添加自己的滚动条。

步骤3:在CellValueNeeded例程中,回复e.RowIndex + scrollBar.Value

步骤4:对于dataStore,我目前打开一个Stream,在CellValueNeeded例程中,首先执行Seek()和Read()所需的数据。

通过这些步骤,我可以非常合理地在dataGrid中滚动非常大的文件(测试高达0.8GB)。

总而言之,看起来减速的实际原因并不是我们保持Seek()和Read(),而是实际的dataGridView本身。

答案 2 :(得分:1)

管理可以汇总,小计,用于多列计算等的行和列,提出了一系列独特的挑战;将问题与编辑所遇到的问题进行比较并不公平。自VB6起,第三方数据网格控件一直在解决客户端显示和操作大型数据集的问题。使用按需加载或自包含的客户端garguantuan数据集来获得非常快速的性能并不是一项微不足道的任务。按需加载可能会受到服务器端延迟的影响;操纵客户端上的整个数据集可能会受到内存和CPU限制的影响。一些支持即时加载的第三方控件提供客户端和服务器端逻辑,而其他控制尝试100%客户端解决问题。

答案 3 :(得分:1)

由于.net位于本机操作系统之上,因此从磁盘到内存的数据运行时加载和管理需要另一种方法。 了解原因和方式:http://www.codeproject.com/Articles/38069/Memory-Management-in-NET

答案 4 :(得分:0)

为了解决这个问题,我建议不要一次加载所有数据。而是以块的形式加载数据,并在需要时显示最相关的数据。我刚做了一个快速测试,发现设置DataSource的{​​{1}}属性是一个很好的方法,但是行数很多,也需要时间。因此,使用DataTable的DataGridView函数以块的形式加载数据,并向用户显示最相关的数据。 Here我已经展示了一个可以帮助你的例子。