FileStream.Close()没有立即关闭文件句柄

时间:2015-05-28 09:41:54

标签: c# .net

我正在以块的形式阅读源文件并将其传递给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;
            }
        }

2 个答案:

答案 0 :(得分:2)

最终的示例代码有帮助,我很确定我有你的问题。

问题是,即使最新的示例代码也无法重现。但是,它显示了您可能错过的另一件事 - 您正在编写的文件是.exe。为什么这是个问题?好吧,有几个原因,但其中一个原因是,当您列出.exe文件使用资源管理器的目录时,资源管理器继续并尝试读取它(获取图标)。在这么短的时间内,无法使用FileShare.None打开文件(事实上,FileShare.Read可能也无济于事,因为打开它的人很可能没有指定FileShare.ReadWrite)。

再一次,FileStream关闭得很好,并且效果很好(尽管摆脱了FlushClose次调用 - 他们浪费了性能,而且毫无用处)。问题是另一个进程在此期间尝试读取该文件。它可能是我的情况下的一些文件管理器(Explorer,Total Commander,...),它可能是某些FileSystemWatcher你可能是某个地方,它可能是病毒扫描程序(尽管现在大多数病毒扫描程序都使用卷影副本) ,它可能是自动为图像等创建缩略图的东西。但是你自己发布的代码根本不会导致问题 - 这是其他人抓住你的文件。

基本上有两个选项 - 要么在整个需要时保持文件打开,要么将IOException视为临时文件,并在给定的时间间隔内重试几次。这就是你应该做的事情,而不是依赖于快乐的道路 - 大多数读者只允许并发读取,而不是写入。

答案 1 :(得分:0)

在我的情况下,缺少FileShare模式。我曾经在很短的时间内从同一个文件中的多个源读/写。有时它传递良好,有时它被阻止(另一个进程持有文件),即使文件流已被释放,我正在使用锁和using()。当我添加FileShare模式ReadWrite时,一切都解决了。

using (FileStream fileStream = new FileStream(file, mode, access, FileShare.ReadWrite))
{...