使用Directory.Delete()和Directory.CreateDirectory()覆盖文件夹
在我的WebApi
操作方法中,我想使用以下代码创建/覆盖文件夹:
string myDir = "..."; if(Directory.Exists(myDir)) { Directory.Delete(myDir, true); } Directory.CreateDirectory(myDir); // 1 - Check the dir Debug.WriteLine("Double check if the Dir is created: " + Directory.Exists(myDir)); // Some other stuff here... // 2 - Check the dir again Debug.WriteLine("Check again if the Dir still exists: " + Directory.Exists(myDir));
问题
奇怪的是,有时在创建目录后,该目录不存在!
有时第一次检查目录时(数字1是); Directory.Exist()
返回true
,其他时间返回false
。 第二次检查目录时(数字2的位置)也是如此。
笔记
- 这部分代码都没有抛出任何exception。
- 只有在服务器上发布网站时才能重现这一点。 (Windows server 2008)
- 访问同一文件夹时发生。
问题
- 这是并发问题竞争条件吗?
-
WebApi
或操作系统不处理并发吗? - 这是覆盖文件夹的正确方法吗?
- 当我们对同一个文件有很多API请求时,我是否应该手动锁定文件?
或者一般情况:
- 这种奇怪行为的原因是什么?
更新:
-
使用
DirectoryInfo
和Refresh()
而不是Directory
不能解决问题。 -
仅当Delete的递归选项为
true
时才会发生。 (并且目录不为空)。
许多文件系统操作在某些文件系统上是不同步的(在Windows的情况下 – NTFS)。 以RemoveDirectory调用为例(在某些时候由Directory.DeleteDirectory调用):
RemoveDirectory函数在关闭时标记要删除的目录。 因此,在关闭目录的最后一个句柄之前,不会删除该目录。
如您所见,它将不会真正删除目录,直到它的所有句柄都关闭,但Directory.DeleteDirectory将完成。 在您的情况下,这也很可能是这样的并发问题 – 在您执行Directory.Exists时,并未真正创建目录。
因此,只需定期检查您需要什么,不要认为.NET中的文件系统调用是同步的。 在某些情况下,您还可以使用FileSystemWatcher来避免轮询。
编辑:我在想如何重现它,这是代码:
internal class Program { private static void Main(string[] args) { const string path = "G:\\test_dir"; while (true) { if (Directory.Exists(path)) Directory.Delete(path); Directory.CreateDirectory(path); if (!Directory.Exists(path)) throw new Exception("Confirmed"); } } }
您会看到,如果所有文件系统调用都是同步的(在.NET中),则此代码应该没有问题地运行。 现在,在运行该代码之前,在指定路径创建空目录(最好不要使用SSD)并使用Windows资源管理器打开它。 现在运行代码。 对我来说,它会抛出Confirmed(它完全重现你的问题)或抛出Directory.Delete说该目录不存在(几乎相同的情况)。 它100%的时间对我来说。
这是另一个代码,当我在我的机器上运行时,确认File.Exists肯定可以在File.Delete调用之后直接返回true:
internal class Program { private static void Main(string[] args) { while (true) { const string path = @"G:\test_dir\test.txt"; if (File.Exists(path)) File.Delete(path); if (File.Exists(path)) throw new Exception("Confirmed"); File.Create(path).Dispose(); } } }
为此,我打开了G:\ test_dir文件夹,在执行此代码时尝试打开不断出现和消失的test.txt文件。 经过几次尝试,抛出了确认exception(虽然我没有创建或删除该文件,并且在抛出exception后,它已经不存在于文件系统中)。 因此,在多种情况下可能存在竞争条件,我的答案是正确的。
对我来说听起来像是竞争条件。 不知道为什么 – 你没有提供足够的细节 – 但你可以做的是将所有内容包装在lock()语句中,看看问题是否已经消失。 当然,这不是生产就绪的解决方案,它只是一种快速检查方式。 如果它确实是竞争条件 – 您需要重新考虑重写文件夹的方法。 可以创建“GUID”文件夹,完成后 – 使用最新的GUID更新DB以指向最新的文件夹?