【发布时间】:2013-09-09 17:22:36
【问题描述】:
假设有 2 个进程 P1 和 P2,它们访问一个共享文件 Foo.txt。
假设 P2 正在从Foo.txt 读取数据。我不希望 P1 在 P2 读取它时写信给Foo.txt。
所以我想我可以让 P1 写信给Foo.tmp,最后一步,将Foo.tmp 重命名为Foo.txt。我的编程语言是Java
所以我的问题是,这能确保 P2 从 Foo.txt 读取正确的数据吗?一旦P2完成读取文件,是否会提交重命名操作?
编辑
我尝试按如下方式重新创建此场景:
我的 P1 代码是这样的:
File tempFile = new File(path1);
File realFile = new File(path2);
BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
for(int i=0;i<10000;i++)
writer.write("Hello World\n");
writer.flush();
writer.close();
tempFile.renameTo(realFile);
我的 P2 代码是:
BufferedReader br = new BufferedReader(new FileReader(file));
String line = null;
while(true) {
while((line=br.readLine())!=null){
System.out.println(line);
Thread.sleep(1000);
}
br.close();
}
我的示例共享文件:
Test Input
Test Input
Test Input
我几乎同时启动 P1 和 P2(P2 先启动)。
所以根据我的理解,即使P1已经写了一个新的Foo.txt,由于P2已经在读取它,它应该读取旧的Foo.txt内容,直到它重新打开一个BufferedReader到Foo.txt。
但实际发生的是 P2 读取了三次Test Input,正如输入所预期的那样,但之后它读取了 P1 写入的新内容。
P2 的输出:
Test Input
Test Input
Test Input
Hello World
Hello World
Hello World
.
.
.
所以它不能正常工作。我测试这个场景错了吗?我觉得我错过了什么。
【问题讨论】:
-
你有没有考虑过使用数据库来做这样的事情?你会得到一个历史,你的第二个过程可以只是
select text from foo where insertstamp = max(insertstamp) -
@corsiKa,无法使用数据库。有问题的共享文件与许多必须更改的模块交互。
-
这确实是 Unix 中的常用习语(atomic-rename)。尽管在 C 中,
rename()之前通常有一个fsync(),即使在操作系统/硬件崩溃时也能提供排序保证。 -
您的 P2 代码看起来每次循环都在关闭文件,因此下一次迭代将不得不重新打开文件(如果它已被更改,它将获取新文件)。如果没有显式重新打开,我希望它会失败,但我不知道 java,所以我猜它是在隐式重新打开文件?
-
我认为它不会在每次迭代时重新打开文件。我这么说的原因是因为我在 P2 的 1 次迭代后运行 P1(当它打印一次
Test Input时。所以如果它在每次迭代时重新打开文件,在第 2 次迭代时,它应该打印新数据。但它三次打印Test Input,这意味着它没有关闭文件
标签: java unix file-locking