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

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

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

我的下一个想法是拥有某种控制器,该控制器将读取一行并为其分配一个要解析的线程,但我不确定如果线程处理行的速度快于其速度,控制器是否最终会成为瓶颈阅读并分配它们。

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

有帮助吗?

解决方案

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

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

其他提示

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

仅有的 添加特殊线程来处理读取的原因是如果您预计某些行需要很长时间来处理 您希望这些行聚集在文件的单个部分中。当你不需要的时候添加线程间通信是一个 非常糟糕的主意. 。您大大增加了引入意外瓶颈和/或同步错误的机会。

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

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

我的经验是使用 Java,而不是 C#,所以如果这些解决方案不适用,我深表歉意。

我立即想到的解决方案是有一个运行 3 个线程的执行器(使用 Executors.newFixedThreadPool, , 说)。对于从输入文件读取的每一行/记录,在执行器上触发一个作业(使用 ExecutorService.submit)。执行器将为您排队请求,并在 3 个线程之间进行分配。

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

预计到达时间:听起来很像 Wolfbyte 的第二种解决方案。:-)

预计到达时间2: System.Threading.ThreadPool 听起来与 .NET 中的想法非常相似。我从未使用过它,但它可能值得您花时间!

由于处理文件时瓶颈通常在于处理而不是读取,所以我会选择 生产者-消费者 图案。为了避免锁定,我会查看无锁列表。既然你使用的是 C#,你可以看看 Julian Bucknall 的 无锁列表 代码。

@lomaxx

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

不用担心。如果集群“慢”事务是一个问题,那么排队解决方案就是最佳选择。根据平均事务的快慢,您可能还需要考虑一次为每个工作人员分配多条线路。这将减少同步开销。同样,您可能需要优化缓冲区大小。当然,这两项都是优化,您可能只应该在分析之后进行。(如果同步不是瓶颈,则无需担心同步。)

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

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

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

将文件分割成块并解析它。一次只读入与您正在处理的块一样多的块,再加上一些“预读”,这样当您处理完一个块然后再转到下一个块之前,您就不会在磁盘上停顿。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top