gpt4 book ai didi

Java NIO - 内存映射文件

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:41:57 25 4
gpt4 key购买 nike

我最近遇到了这个article它很好地介绍了内存映射文件以及如何在两个进程之间共享它。以下是读取文件的进程的代码:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MemoryMapReader {

/**
* @param args
* @throws IOException
* @throws FileNotFoundException
* @throws InterruptedException
*/
public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException {

FileChannel fc = new RandomAccessFile(new File("c:/tmp/mapped.txt"), "rw").getChannel();

long bufferSize=8*1000;
MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize);
long oldSize=fc.size();

long currentPos = 0;
long xx=currentPos;

long startTime = System.currentTimeMillis();
long lastValue=-1;
for(;;)
{

while(mem.hasRemaining())
{
lastValue=mem.getLong();
currentPos +=8;
}
if(currentPos < oldSize)
{

xx = xx + mem.position();
mem = fc.map(FileChannel.MapMode.READ_ONLY,xx, bufferSize);
continue;
}
else
{
long end = System.currentTimeMillis();
long tot = end-startTime;
System.out.println(String.format("Last Value Read %s , Time(ms) %s ",lastValue, tot));
System.out.println("Waiting for message");
while(true)
{
long newSize=fc.size();
if(newSize>oldSize)
{
oldSize = newSize;
xx = xx + mem.position();
mem = fc.map(FileChannel.MapMode.READ_ONLY,xx , oldSize-xx);
System.out.println("Got some data");
break;
}
}
}

}

}

}

但是,我对这种方法有一些意见/问题:

如果我们只在空文件上执行读取器,即运行

  long bufferSize=8*1000;
MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize);
long oldSize=fc.size();

这将分配 8000 个字节,现在将扩展文件。这返回的缓冲区的限制为 8000,位置为 0,因此,读取器可以继续读取空数据。发生这种情况后,读取器将停止,因为 currentPos == oldSize

假设现在作者进来了(代码被省略,因为大部分代码很简单,可以从网站上引用)——它使用相同的缓冲区大小,所以它会先写入 8000 字节,然后再分配 8000 字节,扩展文件。现在,如果我们假设这个过程在此时暂停,然后我们回到阅读器,那么阅读器会看到文件的新大小并分配剩余部分(从位置 8000 到 1600)并再次开始阅读,读入另一个垃圾……

我有点困惑是否有为什么要同步这两个操作。据我所知,对 map 的任何调用都可能会用一个空缓冲区(填充为零)扩展文件,或者编写者可能只是扩展了文件,但尚未向其中写入任何内容...

最佳答案

我为进程间通信做了很多内存映射文件的工作。我不会推荐 Holger 的#1 或#2,但他的#3 是我所做的。但一个关键点可能是我只与一个作家合作——如果你有多个作家,事情就会变得更加复杂。

文件的开头是一个 header 部分,其中包含您需要的任何 header 变量,最重要的是指向写入数据末尾的指针。编写者应始终在写入一段数据后更新此 header 变量,并且读者永远不应读取超出此变量的内容。所有主流 CPU 都拥有一种叫做“缓存一致性”的东西,可以保证读者看到的内存写入顺序与写入顺序相同,因此如果遵循这些规则,读者将永远不会读取未初始化的内存。 (一个异常(exception)是读取器和写入器位于不同的服务器上 - 缓存一致性在那里不起作用。不要尝试在不同的服务器上实现共享内存!)

更新文件结束指针的频率没有限制 - 它都在内存中,不会涉及任何 I/O,因此您可以更新每条记录或您写入的每条消息。

ByteBuffer 有 'getInt()' 和 'putInt()' 方法的版本,它们采用绝对字节偏移量,所以这就是我用来读取和写入文件结束标记的方法......我从不使用相对使用内存映射文件时的版本。

您不应该使用文件大小或另一种进程间方法来传达文件结束标记,并且在您已经拥有共享内存时没有必要也没有好处。

关于Java NIO - 内存映射文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22153377/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com