在并行读取内存映射文件中的数据?

3
如果映射文件数据完全驻留在物理内存中,通过定义起始/结束字节的部分数量并让单独的线程处理每个部分来并行读取数据是否有任何好处?目标是允许从大型二进制文件频繁快速读取数据。
我进行了一些测试(使用Java NIO),其中每个线程(使用4个线程进行测试)都可以访问mmap的引用,但由于每个线程更改mmaped文件中的内部指针以读取下一组字节,因此这似乎不安全。我正在考虑将一个文件分成4个mmaped块,供每个线程使用。
更新: 为了提供更多上下文信息,最终我要做的是拥有一个数据结构,该数据结构将引用多个mmaped文件,然后这些引用可以提供给某些函数,该函数将进行循环扫描测试值并将它们放入字节缓冲区。
更新: 这是针对只读文件的。
1个回答

1
你可以为每个线程创建不同的FileChannel。每个线程将读取不同的部分。
正如文档所述,FileChannel是线程安全的。
你的代码应该像这样:
package nio;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class HughTest {

    public static void main(String[] args) {

        try (FileChannel inChannel = new RandomAccessFile("file_Path", "r").getChannel()){

            // TODO Change in each thread the chunk size to read
            long fileSize = inChannel.size();
            ByteBuffer buffer = ByteBuffer.allocate((int) fileSize);
            inChannel.read(buffer);
            buffer.flip();
            // Do what you want

            inChannel.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

这段代码在一个独立的线程中读取文件,你需要将代码放入可运行类中,并在构造函数或其他地方传递FileChannel大小以并行读取整个文件,如此问题描述:我能使用FileChannel从不同的线程独立寻找文件吗?

更新

不幸的是,MappedByteBuffer不是线程安全的,因为它是Buffer的子类,如你所见:内存映射文件是否支持并发get/put操作?,因此你必须使用同步机制来实现并行处理。

一种方法是将整个文件复制到临时文件中(这样可以确保文件永远不会被修改),然后使用像这样的可运行实现:

   private class ThreadFileRead implements Runnable {

        private final long ini;
        private final long end;

        public ThreadFileRead(long ini, long end) {
            this.ini = ini;
            this.end = end;
        }

        @Override
        public void run() {
            MappedByteBuffer out = null;

            try {
                out = new RandomAccessFile("FILEPATH", "r").
                        getChannel().map(FileChannel.MapMode.READ_ONLY, ini, end);

                for (long i = ini; i < end; i++)
                {
                    // do work
                }


            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }

这似乎没有使用mmap。这种方法不需要每次调用“read”方法时从磁盘读取吗? - marcin_koss
是的...在每次调用中,这段代码都会从磁盘读取...让我看看是否可以与mmap结合使用。 - Francisco Hernandez
谢谢,这很有帮助。我在考虑最后也许将文件分成尽可能多的mmap,存储对它们的引用,然后每次需要读取时,将这些mmap引用传递给可运行对象。这样做有意义吗? - marcin_koss
这种方法的问题在于,如果任何程序/线程改变文件的某个区域,则会抛出意外异常,因为mmap可以看到文件更改。您可以在此处查看:http://docs.oracle.com/javase/7/docs/api/java/nio/MappedByteBuffer.html。如果您的文件从未被修改过,则这种方法很好,否则您将遇到意外行为。如果您在分割文件时进行复制,则不会遇到这些问题。 - Francisco Hernandez
是的,我正在寻找一个只读的解决方案。我也会在问题中提到这一点。 - marcin_koss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接