2つのプロセスP1とP2があり、それらが共有ファイルFoo.txt
にアクセスするとします。
P2がFoo.txt
から読み取っているとします。 P2が読み取っているときにP1が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はすでにそれを読み取っているので、BufferedReaderをFoo.txtに再度開くまで、古いFoo.txtコンテンツを読み取る必要があります。
しかし、実際に起こることは、入力から予想されるように、P2がTest Input
を3回読み取ることですが、その後、P1によって書き込まれた新しいコンテンツを読み取ります。
P2からの出力:
Test Input
Test Input
Test Input
Hello World
Hello World
Hello World
.
.
.
そのため、正常に機能しません。このシナリオのテストは間違っていますか?何か足りないものがあるような気がします。
UNIXのrename
操作はアトミックです(rename(2)を参照)。 UNIXのmv
コマンドは、ソースパスとターゲットパスが同じ物理デバイス上にある場合、renameを使用します。ターゲットパスが別のデバイス上にある場合、名前の変更は失敗し、mv
はファイルをコピーします(これはアトミックではありません)。
ターゲットファイルパスが存在する場合、rename
はそれをファイルシステムからアトミックに削除し、新しいファイルに置き換えます。ファイルは、参照カウントがゼロになるまで実際には削除されないため、別のプロセスが現在ファイルを読み取っている場合は、古いファイルを読み取り続けます。すべてのプロセスが古いファイルを閉じると、その参照カウントはゼロになり、ファイルストレージスペースが再利用されます。
FileChannel.lock
を使用してみませんか?
ここに例があります:
http://examples.javacodegeeks.com/core-Java/nio/filelock/create-shared-file-lock-on-file/
Foo.txt
が終了した場合、Foo.tmp
をFoo.txt
に移動すると失敗する可能性が高くなります。 (ただし、最初にFoo.txt
を削除してから移動すると、機能するはずです)。何が起こるかというと、すべてのファイルハンドラーが閉じられるまで、ファイルは物理的に削除されません(そのファイルを使用するプロセスはありません)。また、Foo.tmp
からFoo.txt
を残した後、2つのFoo.txtファイルが作成されます。削除されたがまだメモリ内で開かれているもの(基本的に、そのファイルはディスク上に参照を持っていません)と実際にディスク上にあるもの。#1と同じページにいるかどうか教えてください。