c# - 快速创建和删除文件导致异常

标签 c# .net delete-file

我有一些代码在针对 .Net 4.5.2 的单线程进程中运行。此代码创建一个文件,执行一些操作,然后删除该文件。大多数情况下,这工作得很好,但有时会抛出异常。

我已尝试简化并重现以下问题。从屏幕截图中可以看出,它设法创建和删除文件 240 多次,然后因 UnauthorizedAccessException 而失败。有时下面的代码运行没有错误,有时它会在循环中走得更远。那么这里发生了什么?

在生产环境中,文件在网络共享中写入和删除,当使用网络共享时,IOException 如下抛出(所以它在一个稍微不同的地方中断,在 File.Delete 而不是 ):

System.IO.IOException: The process cannot access the file '\\network_location\sub_dir\TMP1.sql' because it is being used by another process.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.File.InternalDelete(String path, Boolean checkHost)

据我了解,using 语句应该调用 dispose,导致文件流被刷新;所以在 using 语句之后,我希望所有数据都写入磁盘。

我怎样才能确保它按照我的预期始终如一地工作?

    static void Main(string[] args)
    {
        string networkFilePath = @"C:\Temp";
        string filename1 = "tmp1.sql";
        string filename2 = "tmp2.sql";

        string tempFilename1 = Path.Combine(networkFilePath, filename1);
        string tempFilename2 = Path.Combine(networkFilePath, filename2);

        int interations = 20000;

        Console.WriteLine("Started");
        try
        {
            for (int i = 0; i < interations; i++)
            {
                using (var s = new StreamWriter(tempFilename1))
                {
                    s.Write("aaa");
                }

                using (var s = new StreamWriter(tempFilename2))
                {
                    s.Write("aaa");
                }

                FileCompare(tempFilename1, tempFilename2);

                File.Delete(tempFilename1);
                File.Delete(tempFilename2);
            }
        }
        catch (IOException e)
        {
            Console.WriteLine(e.Message);
        }

        Console.WriteLine("Finished");
        Console.ReadLine();
    }

    public static bool FileCompare(string filename1, string filename2)
    {
        using (var fs1 = new FileStream(filename1, FileMode.Open))
        using (var fs2 = new FileStream(filename2, FileMode.Open))
        {
            return [... ommited for brevity, but just checks the content of file1 vs file2]
        }
    }

enter image description here

编辑 我知道我可以使用临时文件名来避免这个问题……问题是,我如何确定 IO 操作已完成,例如,缓存的数据是在本地还是在网络上写入磁盘?

编辑 我修改了我的代码以关闭每个流,但仍然得到以下信息...... enter image description here

最佳答案

大多数情况下,当某些其他程序使用包括 FileShare.Delete 的宽容共享模式打开同一文件时,就会发生这种情况。此共享允许另一个进程删除文件,但文件不会真正被删除,直到它的所有句柄都关闭(包括由使用 FileShare.Delete 打开它的进程获得的句柄)。此类程序的示例可能是搜索索引器和防病毒软件 - 两者都可能想扫描您创建的新文件,并且都不想锁定文件,因此将以非常宽松的共享模式打开它,包括 FileShare.Delete

当您在已使用 FileShare.Delete 打开的文件上调用 File.Delete 时 - 它会无任何错误地返回并且文件将被标记为删除(并且将在所有句柄关闭后删除)。任何后续访问此类“已删除”文件的尝试都将抛出您在示例中观察到的拒绝访问异常。

您可以使用以下代码轻松重现:

static void Main(string[] args) {
    var tmpFile = Path.GetTempFileName();
    // file is now created
    new Thread(() => {
        using (var fs = new FileStream(tmpFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) {
            // open with permissive share mode, including FileShare.Delete
            // and hold that handle for 5 seconds
            Thread.Sleep(5000);
        }
    }).Start();
    // wait a bit for thread above to grab a handle
    Thread.Sleep(1000);
    // delete, will return fine without any errors            
    File.Delete(tmpFile);
    // attempt to create - will fail with access denied exception you have
    using (var s = new StreamWriter(tmpFile)) {
        s.Write("aaa");
    }
    Console.WriteLine("done");
    Console.ReadLine();
}

因为您不知道另一个进程将持有您要删除的文件的句柄多长时间 - 我认为您所能做的就是检查文件在删除后是否仍然存在,如果是 - 稍等一下,然后再次检查:

File.Delete(tmpFile);
while (File.Exists(tmpFile)) {
    // obviously don't loop forever
    // check for some time then throw exception
    Thread.Sleep(100);
}
// in the example above it will loop for 4 seconds while
// handle is hold by another thread, then finally return
// and subsequent creation of new file will work fine without exceptions

关于c# - 快速创建和删除文件导致异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48098557/

相关文章:

c# - .NET Winforms 应用程序在启动时死掉

java - 文件不会删除

C# 找不到 namespace 名称

c# - Visual Studio 2012 Fakes 缺少必需的引用

c# - 如何在我的所有 Windows 上添加一个公共(public)控件?

c# - Parallel.ForEach 会按照 MaxDegreeOfParallelism=1 的顺序进行处理吗?

java - 用java编写C#委托(delegate)

c# - 从 Sqlite 中检索 ulong 会导致 NotSupportedException

windows - 此记事本 (.bat) 代码有什么作用以及如何工作?

python - 如何使用 Python 删除文件夹中除最后 5 个项目之外的所有文件