【问题标题】:Bizarre directory delete behaviour on SSD driveSSD驱动器上奇怪的目录删除行为
【发布时间】:2011-08-26 18:13:47
【问题描述】:

目录 c:\test 里面有 50 个左右的文件,没有子目录。

    If IO.Directory.Exists("C:\test") Then
        IO.Directory.Delete("C:\test", True)
    End If

    IO.Directory.CreateDirectory("C:\test")

驱动器 C 是 Intel 的 X25-M80 SSD 驱动器,操作系统是 Windows 7 64 位,支持 TRIM,Visual Studio 是 2008,目标框架 3.5。执行上述代码时,CreateDirectory 会在没有(可见)异常的情况下中断代码执行。经过一番头疼后,我发现在代码执行到达 CreateDirectory 时删除尚未完成。如果我这样修改我的代码:

    If IO.Directory.Exists("C:\test") Then
        IO.Directory.Delete("C:\test", True)
    End If
    Threading.Thread.Sleep(2000)
    IO.Directory.CreateDirectory("C:\test")

然后一切都按预期进行。

除了明显的 WTF 之外,我的问题是:

  • 无论驱动器是什么,IO.Directory.Delete 都不应该是阻塞函数调用
  • 由于启用了 TRIM 支持,SSD 在删除时是否“作弊”?

【问题讨论】:

  • 出于好奇,如果您在删除目录后立即再次测试If IO.Directory.Exists("C:\test") 会发生什么?
  • 如果我将 Thread.Sleep 替换为 Debug.WriteLine(IO.Directory.Exists("C:\temp") 它将返回 False 并且 CreateDirectory 成功。如果我删除这个 Exists 检查 CreateDirectory 会失败,如所述。
  • 在我的 X25-M160 和 60 GB OCZ Vertex Turbo 上运行良好。你能显示两个线程的调用堆栈吗?
  • 听起来“某事”是惰性写入,可能是内核、控制器或 SSD 本身!
  • 作为一种变通方法,如果您将 test 重命名为 test-deleting、删除它并调用 create 会怎样?

标签: .net file directory delete-file


【解决方案1】:

我之前遇到过这个问题,但这并不是 SSD 驱动器特有的。你最好先移动然后删除:

if(Directory.Exists(dirpath))
{
    string temppath = dirpath + ".deleted";
    Directory.Move(dirpath, temppath);
    Directory.Delete(temppath, true);
}
Directory.Create(dirpath);

另一种处理方法是循环直到完成:

if(Directory.Exists(dirpath))
{
    Directory.Delete(dirpath, true);
    int limit = 100;
    while(Directory.Exists(dirpath) && limit-- > 0)
        Thread.Sleep(0);
}
Directory.Create(dirpath);

【讨论】:

  • 您不能将 Delete 放在循环中,因为它会抛出该目录,因为操作系统已经计划删除该目录。好吧,我想你可以尝试/抓住它,但这有什么意义呢?
  • 我接受了您的回答,因为我在发布我的问题时正在考虑您的第二个解决方案。基线是 IO.Directory.Delete 不能单独信任
  • 我可以确认非 SSD 驱动器也受到影响。感谢您的回答。
  • 我对第一段代码投了赞成票,因为它完美运行并解决了所有问题。但是,您应该将代码更新为 Directory.CreateDirectory 以确保其准确无误。
  • 很好,我要借你的招式然后删除
【解决方案2】:

在使用反射器探索 System.IO.Directory 之后,看起来 .Delete 只是 FindFirstFile、FindNextFile 和 RemoveDirectory Win API 调用的包装器。 .Net 运行时调用这些 API 调用或 API 实现本身没有任何线程或异步。

现在,假设它以某种方式存在 TRIM 问题,您可以通过打开提升的命令提示符并使用 fsutil 来禁用 TRIM:

fsutil behavior set disabledeletenotify 1

要启用,请使用 0 作为参数运行相同的命令。

要查询,请使用 query 作为命令参数:

fsutil behavior query disabledeletenotify 

【讨论】:

    【解决方案3】:

    是的,它与 SSD 驱动器无关。我有同样的问题,但只在客户端笔记本电脑上。我正在使用 .NET 3.5。就我而言,该目录只有一个文件。 CreateDirectory 似乎在 Delete 完成之前首先在内部执行。

    这是一个在许多计算机上运行了三年的代码。客户换了一台新的笔记本电脑,代码对他来说一直失败。我无法在具有相同配置的开发/测试机器上重现该场景。

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题。我最终只删除了目录@"C:\test" 的内容并将新文件复制到目录中。那是我的问题:

      Using Directory.Delete() and Directory.CreateDirectory() to overwrite a folder

      【讨论】:

        【解决方案5】:

        我怀疑您在 NTFS 中发现了以前未公开的竞争条件,因为不存在足够快的驱动器来命中它。我不认为TRIM与它有任何关系(尽管我保留错误的权利!)

        不管怎样,处理这个问题的正确方法是把代码放在一个重试循环中:

        int retries = 3;
        while(true) {
            try {
                doTheOperation();
                break;
            } catch (Exception ex) {
                retries--;
                if (retries == 0) {
                    throw;
                }
        
                Thread.Sleep(100);
                continue;
            }        
        }
        

        【讨论】:

        • 竞争条件...但这看起来不奇怪吗?该目录在调用下一行之前没有完成删除。这意味着操作更慢而不是更快,这对于 SSD 擦除/垃圾收集可能是正确的,也可能不是。
        • @Ritch NTFS 是一款非常、非常复杂的软件,它可以完成您从未想过的事情,例如隧道 (blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx)。如果它只是“告诉你它已经完成”,但没有完全完成它的簿记,我不会感到惊讶
        • 当然,我知道用户->驱动系统是一个复杂的堆栈,但我只是发现速度参数......很难。
        • @Ritch 好吧,归根结底,为什么它不起作用的论点是学术性的,除非你有很多钱买一个提交修补程序请求的 Microsoft Premier 合同;您将不得不添加延迟 :) 由于 AppCompat,许多 NTFS 行为很奇怪(即修复某些东西会破坏应用程序,即使它并不总是 NTFS 的错),所以我们必须忍受它,至少直到下一个Windows 的主要修订。我知道这是一个蹩脚的答案......
        【解决方案6】:

        如果这确实是应用程序代码,请注意您实际上是在尝试删除名为“est”的目录!

        将路径转义为 "c:\test" 或使用 @ 运算符 @"c:\test"。

        【讨论】:

        • 你知道 VB.net 和 C# 语法的区别吗?
        猜你喜欢
        • 1970-01-01
        • 2012-10-19
        • 1970-01-01
        • 1970-01-01
        • 2014-10-06
        • 1970-01-01
        • 1970-01-01
        • 2016-07-15
        • 1970-01-01
        相关资源
        最近更新 更多