2011-10-19 6 views
15

ファイルのフォルダとそのファイルの長さを追跡していますが、少なくとも1つのファイルに書き込みが行われています。現在のファイル長の取得/ FileInfo.Lengthのキャッシュと古い情報

私は他の目的のために使用する各ファイルの長さの連続的に更新された記録を保持する必要があります。

Updateメソッドは15秒ごとに呼び出され、ファイルの長さが前の更新で決定された長さと異なる場合、ファイルのプロパティを更新します。

更新方法は、次のようなものになります。

var directoryInfo = new DirectoryInfo(archiveFolder); 
var archiveFiles = directoryInfo.GetFiles() 
           .OrderByDescending(f=>f.CreationTimeUtc); 
foreach (FileInfo fi in archiveFiles) 
{ 
    //check if file existed in previous update already 
    var origFileProps = cachedFiles.GetFileByName(fi.FullName); 
    if (origFileProps != null && fi.Length == origFileProps.EndOffset) 
    { 
     //file length is unchanged 
    } 
    else 
    { 
     //Update the properties of this file 
     //set EndOffset of the file to current file length 
    } 
} 

私はDirectoryInfo.GetFiles()であるという事実を認識していLength含むFileInfo性質の多くを事前移入を - と全くキャッシュが行われませんので、これは限りokですの間の更新(キャッシュされた情報は15秒を超えてはなりません)。

私は、各DirectoryInfo.GetFiles()呼び出しはすべてがFindFirstFile/FindNextFileのWin32 APIを使用して右クリック、新鮮な情報が移入されているFileInfos新しいセットを生成し、仮定の下にありました。しかし、これはそうではないようです。非常にまれ

が、最終的には確かに私はテストは、Windows 2008 Serverのx64の上で行われている(に書き込まなっているファイルのファイルの長さは、一度に5、10、あるいは20分に更新されていないような状況に遭遇それが問題なら)。

現在の回避策は、fi.Refresh()を呼び出して、各ファイル情報を強制的に更新することです。これは内部的にファイル情報を更新するためにWin32 API呼び出しGetFileAttributesExに委任しているようです。

手動でリフレッシュを強制するコストは許容できますが、私はむしろ理解しています。なぜ私は古い情報を最初に取得しています。 FileInfoの情報はいつ生成され、DirectoryInfo.GetFiles()のコールにはどのような関係がありますか?私が完全に把握していないファイルI/Oキャッシュ層がありますか?

答えて

14

レイモンドチェンは今、まさにこの問題について非常に詳細なブログ記事を書いています:NTFSで

Why is the file size reported incorrectly for files that are still being written to?

ファイルシステムのメタデータではなく、ディレクトリエントリ のではなく、むしろの財産でありますディレクトリの列挙を改善するために、 ディレクトリエントリにレプリケートされたメタデータの一部があります。 パフォーマンス。 FindFirstFileのような関数はディレクトリ のエントリを報告し、FATユーザーが慣れていた のメタデータを "無料"にすることで、 ディレクトリリストのFATよりも遅くならないようにすることができます。 ディレクトリ列挙関数は、最後に更新されたメタデータ を報告します。これは、ディレクトリエントリが失効している場合、実際のメタデータ に対応しない可能性があります。

は、基本的にそれがパフォーマンスにダウンしています:ディレクトリ情報がDirectoryInfo.GetFiles()から収集し、パフォーマンス上の理由は、ディレクトリ情報を取得するための古いFATよりもNTFSで優れた性能を保証するためFindFirstFile/FindNextFileのWin32 APIを下にキャッシュされます。正確なファイルサイズ情報は、Get­File­Size()を直接ファイルに呼び出し(FileInfoの.NETコールRefresh())、またはファイル名から直接FileInfoを取得することによってのみ取得できます。ディレクトリメタデータキャッシュに伝播されます。後のケースでは、書き込みプロセスがファイルを閉じるときにファイルサイズがすぐに更新される理由を説明します。

: - キャッシュがフラッシュされた時はいつでもそのファイルの情報がより頻繁に/複製されたバック - これは、Windows 2008 Serverのもうケースではありません

これも問題は一見のWindows 2003 Serverで現れなかったと説明しています

回答の頻度は、もう少し複雑です。 Windows Vista(およびそれに対応するWindowsサーバーのバージョン、 はわかりませんが、私はあなたがルックアップできると確信しています。 "Yuhong Bao"を意味します)、NTFSファイルシステムはこの礼儀を実行します ファイルオブジェクトの最後のハンドルが閉じられたときの複製。 以前のバージョンのNTFS は、キャッシュが でフラッシュされるたびにファイルが開かれている間にデータを複製したため、予期しないスケジュールである に従って非常に頻繁に発生しました。この変更の結果、 ディレクトリエントリの更新頻度が低下するため、最後に更新されたファイルサイズは よりも古くなりました。

記事全文を読むことは非常に参考になります。

5

私はあなたがFileSystemWatcherを使用し、変更イベントを購読する必要があります。指定されたファイルシステム項目が変更されたときにトリガされます。

+0

+1この場合、私は回避策がありますし、長期的にはFileSystemWatcherの使用をリファクタリングする可能性が最も高いですが、これはなぜ*私が古い情報を取得しているのかこの質問はすべてについてです – BrokenGlass

+0

IMOいくつかのOSの現金化レイヤーが必要です。 stream.Flush()を呼び出しても、HDでの保存は強制されません。ディスク書き込みキャッシングを無効にしようとしましたか? http://support.microsoft.com/kb/259716 – Wojteq

+0

他の説明が不足しているので、私は当面の対応策としてこの回答を受け入れます。 – BrokenGlass

1

私はWojteqにFileSystemWatcherクラスを使用する方が良い解決法であることに同意します。これは、ファイルまたはディレクトリのさまざまな属性が変更されたときのイベント(彼が参照したChangeイベントなど)を公開し、現在実行されているポーリングソリューションより優れたソリューションです。なぜRefreshがファイルのサイズの変化を反映するために時間を変えるのかについてのあなたの質問に答えるためには、Windowsオペレーティングシステムの基本的な仮想メモリマネージャと関係があります。ファイルI/Oが実行されると、メモリマップされたファイルに対して実際に更新が行われます。これはオペレーティングシステムによって管理されるファイルのバッファされたコピーです。したがって、Windowsは、バッファリングされたデータがディスクに書き込まれるときに制御します。特定のバッファされたデータが物理的にディスクに書き込まれる時期を予測する方法はありません。つまり、ファイルストリームを更新すると、それらの更新がバッファに格納されます。ストリームをFlush()する場合、バッファリングされた更新はすぐにディスクに書き込まれます。ストリームを閉じると、ストリームが閉じられた直後にバッファからディスクに書き込まれ、ストリームが開いている場合バッファリングされたデータをディスクに書き込むことを決定するとWindowsに転送されます。

+0

バッファリングは更新遅延を数分ではなく数分で説明します。書き込みコードはC言語であり、デフォルトでは数キロバイト以下のAFAIKというバッファサイズを使用する 'fwrite'を使用します。 – BrokenGlass

+0

+1確かにまだ書かれているファイルと何か関係がありますが、書込みプロセスが止まったらすぐに正しいアップデートを得ることができました。古くなった情報の問題は非常にまれに起こりますが、それは依然として質問*なぜ*これが最初に起こるか – BrokenGlass