2つの解決策が思い浮かびます。
最初の最も簡単な方法は、スレッドにファイルの名前を他のスレッドが触れない名前に変更させることです。filename.dat.<unique number>
" " のようなもので、<unique number>
はスレッド固有のものです。その後、スレッドは必要なだけファイルをパーティーできます。
2 つのスレッドが同時にファイルを取得した場合、そのファイルの名前を変更できるのはそのうちの 1 つだけです。他のスレッドで発生する IOException を処理する必要がありますが、問題にはなりません。
もう 1 つの方法は、単一のスレッドでディレクトリを監視し、ファイル名をBlockingCollection
. ワーカー スレッドは、そのキューから項目を取得して処理します。その特定の項目をキューから取得できるのは 1 つのスレッドだけなので、競合は発生しません。
このBlockingCollection
ソリューションは、セットアップが少し (ほんの少しだけ) 複雑ですが、複数のスレッドが同じディレクトリを監視するソリューションよりも優れたパフォーマンスを発揮するはずです。
編集
あなたの編集された質問は、問題をかなり変えます。一般にアクセス可能なディレクトリにファイルがある場合、そこに配置されてからスレッドがロックするまでの任意の時点で、ファイルが表示、変更、または削除されるリスクがあります。
ファイルを開いている間はファイルを移動したり削除したりできないため (私が認識していません)、最善の策は、スレッドにファイルを公開されていないディレクトリに移動させることです。理想的には、アプリケーションを実行しているユーザーのみがアクセスできるようにロックダウンされたディレクトリです。したがって、コードは次のようになります。
File.Move(sourceFilename, destFilename);
// the file is now in a presumably safe place.
// Assuming that all of your threads obey the rules,
// you have exclusive access by agreement.
編集#2
別の可能性は、ファイルを排他的に開き、独自のコピー ループを使用してコピーし、コピーが完了したときにファイルを開いたままにすることです。その後、ファイルを巻き戻して処理を行うことができます。何かのようなもの:
var srcFile = File.Open(/* be sure to specify exclusive access */);
var destFile = File.OpenWrite(/* destination path */);
// copy the file
var buffer = new byte[32768];
int bytesRead = 0;
while ((bytesRead = srcFile.Read(buffer, 0, buffer.Length)) != 0)
{
destFile.Write(buffer, 0, bytesRead);
}
// close destination
destFile.Close();
// rewind source
srcFile.Seek(0, SeekOrigin.Start);
// now read from source to do your processing.
// for example, to get a StreamReader, just pass the srcFile stream to the constructor.
場合によっては、処理してからコピーできます。処理が終了したときにストリームが開いたままかどうかによって異なります。通常、コードは次のようなことを行います。
using (var strm = new StreamReader(srcStream, ...))
{
// do stuff here
}
これにより、ストリームと srcStream が閉じられます。次のようにコードを書く必要があります。
using (var srcStream = new FileStream( /* exclusive access */))
{
var reader = new StreamReader(srcStream, ...);
// process the stream, leaving the reader open
// rewind srcStream
// copy srcStream to destination
// close reader
}
実行可能ですが、不器用です。
ああ、ファイルを削除する前に誰かがファイルを読み取る可能性を排除したい場合は、ファイルを閉じる前に 0 で切り捨ててください。次のように:
srcStream.Seek(0, SeekOrigin.Begin);
srcStream.SetLength(0);
そうすれば、削除する前に誰かがそれに到達した場合でも、変更する必要はありません。