考虑简单的Java应用程序,该应用程序应遍历光盘中的文件树,以在文件正文中查找特定模式。
想知道是否有可能使用多线程获得更好的性能,例如当我们找到新文件夹时,我们在固定的ThreadPool中提交新的Runnable。 Runnable任务应遍历文件夹以查找新文件夹等。在我看来,此操作应该是IO绑定,而不是CPU绑定,因此生成新线程不会提高性能。
是否取决于硬盘类型? (hdd,...等) 它取决于操作系统类型吗?
恕我直言,唯一可以并行的是 - 生成新的Thread来解析文件内容以找出文件体中的模式。
解决这个问题的常见模式是什么呢?它应该是多线程还是单线程?
答案 0 :(得分:2)
我在测试项目下进行了一些研究,你可以在github上查看项目:http://github.com/4ndrew/filesearcher。当然主要的问题是磁盘I / O速度,但如果您使用最佳线程数来并行执行读取/搜索,您可以获得更好的共同结果。
UPD:另请参阅本文http://drdobbs.com/parallel/220300055
答案 1 :(得分:2)
我前段时间就这个问题做了一些实验。最后,我得出结论,通过改变访问文件的方式,我可以获得更好的改进。
这是我最终使用的文件助手:
// 4k buffer size ... near-optimal for Windows.
static final int SIZE = 4 * 1024;
// Fastest because a FileInputStream has an associated channel.
private static void ScanDataFile(Hunter h, FileInputStream f) throws FileNotFoundException, IOException {
// Use a mapped and buffered stream for best speed.
// See: http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly
FileChannel ch = f.getChannel();
// How much I've read.
long red = 0L;
do {
// How much to read this time around.
long read = Math.min(Integer.MAX_VALUE, ch.size() - red);
// Map a byte buffer to the file.
MappedByteBuffer mb = ch.map(FileChannel.MapMode.READ_ONLY, red, read);
// How much to get.
int nGet;
// Walk the buffer to the end or until the hunter has finished.
while (mb.hasRemaining() && h.ok()) {
// Get a max of 4k.
nGet = Math.min(mb.remaining(), SIZE);
// Get that much.
mb.get(buffer, 0, nGet);
// Offer each byte to the hunter.
for (int i = 0; i < nGet && h.ok(); i++) {
h.check(buffer[i]);
}
}
// Keep track of how far we've got.
red += read;
// Stop at the end of the file.
} while (red < ch.size() && h.ok());
// Finish off.
h.close();
ch.close();
f.close();
}
答案 2 :(得分:1)
您说得对,您需要确定您的任务是CPU还是IO绑定,然后决定是否可以从多线程中受益。通常磁盘操作非常慢,因此除非需要解析和解析复杂性的数据量,否则您可能无法从多线程中受益。我只想写一个简单的测试 - 只是为了读取没有在单个线程中解析的文件,测量它然后添加解析并查看它是否慢得多然后决定。
也许好的设计是使用两个线程 - 一个读取文件并将数据放入(有界)队列的读取器线程,然后另一个线程(或更好地使用ExecutorService)解析数据 - 它会让你很好地分离关注点你总是可以调整解析的线程数。我不确定使用多个线程读取磁盘是否有意义(除非您需要从多个物理磁盘读取等)。
答案 3 :(得分:1)
您可以做的是:实现单生产者多消费者模式,其中一个线程搜索磁盘,检索文件,然后消费者线程处理它们。
你是对的,在这种情况下,使用多个线程来扫描磁盘是没有好处的,事实上它可能会降低性能,因为磁盘每次都需要寻找下一个读取位置,所以你最终弹出磁盘线程之间。