使用多线程对文件进行排序
Posted
技术标签:
【中文标题】使用多线程对文件进行排序【英文标题】:Sorting file with multi threads 【发布时间】:2012-01-27 10:45:03 【问题描述】:我通过读入块(Arraylist)对大文件进行排序,使用带有自定义比较器的 Collections.sort 对每个数组列表进行排序,并将排序结果写入文件,然后对所有文件应用合并排序算法。
我在一个线程中完成。
如果我为每个 Collections.sort() 启动一个新线程,我会得到任何性能提升吗?
我的意思是: 我从文件读入列表,当列表已满时,我启动一个新线程,对列表进行排序并写入临时文件。
同时我继续从文件中读取并在列表再次满时启动一个新线程......
我还有一个问题:
什么对排序更好: 1) 我填写的 Arraylist 和满时应用 collections.sort() 2)我填写的TreeMap,我不需要对其进行排序。 (在我插入项目时排序)
注意:我使用 JAVA 1.5
更新: 这是我要使用的代码,问题是我正在重用线程正在使用的数据线数组列表,而且我需要等到所有线程完成。 我该如何解决?
int MAX_THREADS = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);
List datalines = ArrayList();
try
while (data != null)
long currentblocksize = 0;
while ((currentblocksize <= blocksize) && (data = getNext()) != null)
datalines.add(data);
currentblocksize += data.length();
executor.submit(new Runnable()
public void run()
Collections.sort(datalines,mycomparator);
vector.add(datalines);
);
【问题讨论】:
列表中的“完整”是什么意思?您意识到,当您对这些子列表进行排序时,您不会对整个文件进行排序,对吧? 我的意思是我将列表限制为 1000 行文本。在我对子文件进行排序并将它们写入子文件后,我计划对它们进行合并排序。 啊,好吧 - 我错过了那一点。这个文件有多大?您真的需要将子列表写入文件吗? 是的,文件很大我无法在内存中排序 我看到你提到了“文本行”。我有 3 个问题: 1. 这个大文件是文本文件吗? 2. 你打开它是文本文件还是二进制文件? 3. 您使用的是什么操作系统? 【参考方案1】:我建议您实施以下方案,称为农场:
worker0
reader --> worker1 --> writer
...
workerN
因此,一个线程从文件中读取一个块,将其交给工作线程(最佳实践是让工作线程作为ExecutorService
)对其进行排序,然后每个工作线程将其输出发送到写入线程以放置在临时文件中。
编辑:好的,我查看了您的代码。要解决共享datalines
的问题,您可以为每个存储线程需要排序的当前datalines
数组的线程设置一个私有成员:
public class ThreadTask implements Runnable
private List datalines = new ArrayList();
public ThreadTask(List datalines)
this.datalines.add(datalines);
public void run()
Collections.sort(datalines,mycomparator);
synchronized(vector)
vector.add(datalines);
您还需要同步对共享vector
集合的访问。
然后,等待 ExecutorService 中的所有线程使用完毕:
executor.awaitTermination(30, TimeUnit.SECONDS);
【讨论】:
还有一件事,我应该将IO文件写入线程中吗? @simpleuser: 使用多个线程向同一个磁盘写入(或读取)绝不是一个好主意,因为磁盘需要寻找下一个写入位置,如果出现以下情况,您将继续在两次寻找之间弹跳磁盘您使用多个线程。你应该做的是让另一个单独的线程从队列中获取排序后的数组(就像我放的图片一样)并将它们写入磁盘。【参考方案2】:使用线程是否会加快速度取决于您是否受到磁盘 I/O 或 CPU 速度的限制。这取决于您的磁盘有多快(SSD 比旋转硬盘快得多),以及您的比较功能有多复杂。如果限制是磁盘 I/O,那么添加线程或担心数据结构是没有意义的,因为这些不会帮助您更快地从磁盘读取数据。如果限制是 CPU 速度,您应该首先运行分析器以确保您的比较函数没有做任何缓慢和愚蠢的事情。
【讨论】:
我以前也用过 Eclipse。但是当我需要分析时,我下载了 NetBeans,安装它,用它分析,关闭它,切换回 Eclipse,然后再也没有使用它。【参考方案3】:第一个问题的答案是——是的。如果您实现合并排序的并行版本,您将获得性能提升。在这篇 Dr.Dobbs 文章中了解更多信息:http://drdobbs.com/parallel/229400239。
【讨论】:
【参考方案4】:如果您的进程受 CPU 限制(我怀疑不是),您可以看到使用多个线程的改进。如果您的进程是 IO 绑定的,则需要提高您的 IO 带宽和运行速度。
【讨论】:
【参考方案5】:并行化顺序操作将在三种情况下提高性能:
-
您有一个受 CPU 限制的应用程序,并且有多个内核可以在没有协调的情况下工作。在这种情况下,每个核心都可以完成它的工作,你会看到线性加速。但是,如果您没有多个内核,多线程实际上会减慢您的速度。
您有一个 IO 绑定应用程序,您在其中通过独立通道执行 IO。应用程序服务器与多个套接字交互就是这种情况。给定套接字上的数据相对不受其他套接字上发生的任何事情的阻碍。通常不是磁盘 IO 的情况,除非您可以确保您的磁盘操作将分离主轴,并可能分离控制器。您通常不会在这里看到太多的加速,因为应用程序仍将花费大量时间等待。但是,它可以带来更简洁的编程模型。
您将 IO 和 CPU 交错。在这种情况下,一个线程可以执行 CPU 密集型操作,而另一个线程等待 IO。加速(如果有的话)取决于应用程序中 CPU 和 IO 之间的平衡;在许多(大多数)情况下,与 IO 相比,CPU 的贡献可以忽略不计。
您描述了案例 #3,并确定您需要测量 CPU 与 IO 的答案。一种方法是使用分析器:如果您 90% 的时间都在 FileInputStream.read()
,那么您不太可能获得加速。但是,如果您有 50% 的时间在那里,而 50% 的时间在 Arrays.sort()
,那么您会的。
但是,我看到您的一个 cmets,您说您正在解析比较器内的行。如果是这种情况,并且Arrays.sort()
需要花费大量时间,那么我敢打赌,您会通过读取解析获得更多的速度提升。
【讨论】:
以上是关于使用多线程对文件进行排序的主要内容,如果未能解决你的问题,请参考以下文章