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

标签 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;
            }
        }

最佳答案

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

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

再一次,FileStream 关闭得很好,并且运行良好(摆脱了 FlushClose 调用,尽管 - 它们'浪费性能,而且没用)。问题是另一个进程试图同时读取该文件。它可能是一些文件管理器,如我的情况(Explorer、Total Commander 等),它可能是你在某个地方的一些 FileSystemWatcher,它可能是一个病毒扫描程序(尽管现在大多数病毒扫描程序都使用卷影副本),它可能会自动为图像等创建缩略图。但是您自己发布的代码根本不会导致问题 - 其他人正在获取您的文件。

您基本上有两个选择 - 要么在您需要文件的整个过程中一直打开该文件,要么将 IOException 视为临时的并在给定的时间间隔内重试几次。无论如何,这就是您应该做的,而不是依赖快乐的路径 - 大多数读者只允许并发读取,而不是写入。

关于c# - FileStream.Close() 没有立即关闭文件句柄,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30502845/

相关文章:

c# - DataVisualization.Chart 的图形和工具提示的样式化

c# - 使用 MVVM 以编程方式创建 XAML Canvas

c# - 使用 RSA SHA 256 对字符串进行签名,仅使用私钥作为字符串 .NET

c# - 可观察管道中的异常处理

c# - 重新发明标签控件

c# - .NET PInvoke 在 Linux 和 Mac OS X 平台上可用吗?

c# - Masked TextBox 如何不在文本框中显示下划线并允许用户输入 3 位数字

c# - VS2010 Crystal 报表 : How do I set the logging level?

.net - 如何在 AfterBuild 中将 ILMerge 与 .NET 4.5 结合使用?

c# - 是否有良好的 API 设计模式将写入与读取分开?