多核文本文件解析

时间:2008-08-10 03:07:39

标签: c# multithreading

我有一个四核机器,并且想编写一些代码来解析利用所有四个核心的文本文件。文本文件基本上每行包含一条记录。

多线程不是我的强项,所以我想知道是否有人能给我一些我可以用来以最佳方式解析文件的模式。

我的第一个想法是将所有行读入某种队列,然后启动线程以从队列中拉出线并处理它们,但这意味着队列必须存在于内存中,这些是相当大的文件所以我不是那么热衷于这个想法。

我的下一个想法是让某种控制器在一行中读取并为其分配一个解析的线程,但我不确定如果线程更快地处理线程,控制器是否会成为瓶颈它可以阅读和分配它们。

我知道可能还有一个比这两个更简单的解决方案,但目前我还没有看到它。

7 个答案:

答案 0 :(得分:9)

Mark的答案是更简单,更优雅的解决方案。如果没有必要,为什么要使用线程间通信来构建复杂的程序?产生4个线程。每个线程计算文件大小/ 4以确定它的起点(和停止点)。然后,每个线程可以完全独立工作。

添加特殊线程来处理读取的原因是,如果您希望某些行花费很长时间来处理,您希望这些行是群集的在文件的单个部分中。当您不需要它时添加线程间通信是非常糟糕的主意。您极大地增加了引入意外瓶颈和/或同步错误的可能性。

答案 1 :(得分:8)

我会按照你原来的想法去做。如果您担心队列可能变得太大,请为它实现一个缓冲区(即,如果超过100行,则停止读取文件,如果它低于20,则再次开始读取。您需要进行一些测试找到最佳障碍)。使它成为任何线程都可能成为“读者线程”,因为它必须锁定队列以拉出项目,无论如何它还可以检查“低缓冲区域”是否已被命中并再次开始读取。在执行此操作时,其他线程可以读出队列的其余部分。

或者如果您愿意,让一个读者线程将行分配给其他三个处理器线程(通过他们自己的队列)并实现work-stealing strategy。我从来没有这样做过,所以我不知道它有多难。

答案 2 :(得分:3)

这将消除让单个线程读取的瓶颈:

open file
for each thread n=0,1,2,3:
    seek to file offset 1/n*filesize
    scan to next complete line
    process all lines in your part of the file

答案 3 :(得分:1)

我的经验是使用Java而不是C#,如果这些解决方案不适用,请道歉。

我能想到的最直接的解决方案是拥有一个运行3个线程的执行程序(例如,使用Executors .newFixedThreadPool)。对于从输入文件中读取的每一行/记录,在执行程序中启动作业(使用ExecutorService .submit)。执行程序将为您排队请求,并在3个线程之间进行分配。

可能存在更好的解决方案,但希望能够完成这项工作。 : - )

ETA:听起来很像Wolfbyte的第二个解决方案。 : - )

ETA2:System.Threading.ThreadPool在.NET中听起来非常相似。我从来没有用过它,但它可能值得你这么做!

答案 4 :(得分:1)

由于瓶颈通常是处理过程而不是处理文件时的读数,我会使用producer-consumer模式。为了避免锁定,我会查看锁定免费列表。由于您使用的是C#,因此您可以查看Julian Bucknall的Lock-Free List代码。

答案 5 :(得分:0)

@lomaxx

  

@Derek&马克:我希望有办法接受2个答案。我将不得不最终使用Wolfbyte的解决方案,因为如果我将文件拆分为n个部分,则有可能线程遇到一批“慢”事务,但是如果我正在处理每个进程的文件保证需要等量的处理然后我真的喜欢你的解决方案,只需将文件分成块并将每个块分配给一个线程并完成它。

不用担心。如果群集“慢”事务是一个问题,那么排队解决方案是可行的方法。根据平均事务的速度或速度,您可能还希望一次为每个工作人员分配多行。这将减少同步开销。同样,您可能需要优化缓冲区大小。当然,这两种都是优化,您可能只应在分析后进行优化。 (如果它不是瓶颈,那就不用担心同步了。)

答案 6 :(得分:0)

如果要解析的文本由重复的字符串和标记组成,请将文件分成块,对于每个块,您可以让一个线程将其预先解析为包含关键字,“标点符号”,ID字符串的标记,和价值观。字符串比较和查找可能非常昂贵,如果不需要进行字符串查找和比较,则将其传递给多个工作线程可以加速代码的纯逻辑/语义部分。

预先解析的数据块(您已经完成了所有字符串比较并将其“标记化”)然后可以传递给实际查看标记化数据的语义和排序的代码部分。 / p>

另外,您提到您担心占用大量内存的文件大小。您可以采取一些措施来削减内存预算。

将文件拆分为块并解析它。只读入你正在处理的块数加上一些用于“预读”的块,这样当你在进入下一个块之前完成处理块时,你不会在磁盘上停滞。

或者,大文件可以进行内存映射并加载“需求”。如果你有更多的线程处理文件而不是CPU(通常线程= 1.5-2X CPU对于请求分页应用程序是一个很好的数字),那么在内存映射文件的IO上停止的线程将自动从操作系统停止,直到它们的内存已准备就绪,其他线程将继续处理。