想到了两个解决方案。
第一个也是最简单的方法是让线程将文件重命名为其他线程不会触及的名称。诸如“ filename.dat.<unique number>
”之类<unique number>
的东西,特定于线程的东西在哪里。然后线程可以随心所欲地处理文件。
如果两个线程同时获取文件,则只有其中一个线程能够重命名它。您必须处理其他线程中发生的 IOException,但这应该不是问题。
另一种方法是让一个线程监视目录并将文件名放入BlockingCollection
. 工作线程从该队列中获取项目并处理它们。因为只有一个线程可以从队列中获取该特定项目,所以没有争用。
该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);
这样,如果有人在您删除它之前确实找到了它,那么就没有什么可修改的了,等等。