我正在以块的形式阅读源文件并将其传递给WCf服务以记录在一些远程SMB上。我一直打开FileStream,直到写完所有数据。
打开和关闭文件句柄多次降低性能,所以我遵循这种方法。
写完所有数据后,我调用CloseHandle()。然后我可能需要通过调用DoSomeOperation()对同一文件执行一些其他操作。因为我在CloseHandle()函数中关闭了文件句柄,但是我得到了错误"文件正在使用其他一些进程"在DoSomeOperation()中。如果我在延迟一段时间后调用DoSomeOperation(),那么问题就不存在了。
当我调用FileStream.Close()时,请帮助我们立即关闭文件句柄。
此代码段是大型程序的一部分,因此我无法在此处提及所有代码。
//In WCF service
FileStream fs = null;
public void AppendBytes(string fileName, byte[] data, long position)
{
try
{
if (fs==null)//In first call, open the file handle
fs = System.IO.File.Open(fileName, System.IO.FileMode.Append, System.IO.FileAccess.Write, System.IO.FileShare.None);
fs.Write(data, 0, data.Length);
}
catch (Exception ex)
{
//Close handle in case of error
if (fs != null)
fs.Close();
}
}
public void CloseHandle()
{
//Close handle explicitly
if (fs != null)
fs.Close();
}
public void DoSomeOperation(string fileName)
{
using (FileStream fsO = System.IO.File.Open(fileName, System.IO.FileMode.Append, System.IO.FileAccess.Write, System.IO.FileShare.None))
{
//Do something with file here, this is atomic operation so I am opening FileStream with 'using' to dispose at operation is over
}
}
//In client
public void CallerFunction()
{
//Read Data from sourceFile in chunk and copy to target file using WCF.AppendBytes on another machine
WCF.AppendBytes(filename, data, pos);
WCF.CloseHandle();
WCF.DoSomeOperation(filename); //I get error here that file is in use with some other process. if I put a thread.sleep(1000) just before this statement then all works fine.
}
我编写了一个小的测试代码来重现控制台应用程序上的相同场景:只需从Main()调用TestHandleClose(),它会在一些周期后给出错误。
static void TestHandleClose()
{
int i = 0;
try
{
if (File.Exists(@"d:\destination\file2.exe"))
File.Delete(@"d:\destination\file2.exe");
byte[] data = null;
int blocksize = 10 * 1024 * 1024;
for( i=0;i<100;i++)
{
using (FileStream fr = File.Open(@"d:\destination\File1.zip", FileMode.Open, FileAccess.Read, FileShare.None))
{
data = new byte[blocksize];
fr.Read(data, 0, blocksize); //We are reading the file single time but appending same data to target file multiple time.
using (FileStream f = File.Open(@"d:\destination\file2.exe", FileMode.Append, FileAccess.Write, FileShare.None))
{
f.Write(data, 0, data.Length); //We are writing same data multiple times.
f.Flush();
f.Close();
}
}
}
if (File.Exists(@"d:\destination\file2.exe"))
File.Delete(@"d:\destination\file2.exe");
}
catch (Exception ex)
{
throw;
}
}
答案 0 :(得分:2)
最终的示例代码有帮助,我很确定我有你的问题。
问题是,即使最新的示例代码也无法重现。但是,它显示了您可能错过的另一件事 - 您正在编写的文件是.exe
。为什么这是个问题?好吧,有几个原因,但其中一个原因是,当您列出.exe
文件使用资源管理器的目录时,资源管理器继续并尝试读取它(获取图标)。在这么短的时间内,无法使用FileShare.None
打开文件(事实上,FileShare.Read
可能也无济于事,因为打开它的人很可能没有指定FileShare.ReadWrite
)。
再一次,FileStream
关闭得很好,并且效果很好(尽管摆脱了Flush
和Close
次调用 - 他们浪费了性能,而且毫无用处)。问题是另一个进程在此期间尝试读取该文件。它可能是我的情况下的一些文件管理器(Explorer,Total Commander,...),它可能是某些FileSystemWatcher
你可能是某个地方,它可能是病毒扫描程序(尽管现在大多数病毒扫描程序都使用卷影副本) ,它可能是自动为图像等创建缩略图的东西。但是你自己发布的代码根本不会导致问题 - 这是其他人抓住你的文件。
基本上有两个选项 - 要么在整个需要时保持文件打开,要么将IOException
视为临时文件,并在给定的时间间隔内重试几次。这就是你应该做的事情,而不是依赖于快乐的道路 - 大多数读者只允许并发读取,而不是写入。
答案 1 :(得分:0)
在我的情况下,缺少FileShare模式。我曾经在很短的时间内从同一个文件中的多个源读/写。有时它传递良好,有时它被阻止(另一个进程持有文件),即使文件流已被释放,我正在使用锁和using()。当我添加FileShare模式ReadWrite时,一切都解决了。
using (FileStream fileStream = new FileStream(file, mode, access, FileShare.ReadWrite))
{...