在我的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));
问题
奇怪的是,有时在创建目录后,该目录不存在!
有时第一次检查dir时(数字1的位置); Directory.Exist()
返回true
,其他时间false
。第二次检查目录时(数字2的位置)也是如此。
备注
问题
WebApi
或操作系统是否处理并发?或者一般情况:
更新
使用DirectoryInfo
和Refresh()
代替Directory
无法解决问题。
仅当删除的递归选项为true
时才会发生。 (并且目录不为空)。
答案 0 :(得分:10)
许多文件系统操作在某些文件系统上不同步(在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文件。经过几次尝试,抛出了确认异常(虽然我没有创建或删除该文件,但在抛出异常后,它已经不存在于文件系统中)。因此,在多种情况下,竞争条件是可能的,我的答案是正确的。
答案 1 :(得分:1)
我为自己编写了一些C#方法,用于使用Directory.Delete()
进行同步文件夹删除。随时复制:
private bool DeleteDirectorySync(string directory, int timeoutInMilliseconds = 5000)
{
if (!Directory.Exists(directory))
{
return true;
}
var watcher = new FileSystemWatcher
{
Path = Path.Combine(directory, ".."),
NotifyFilter = NotifyFilters.DirectoryName,
Filter = directory,
};
var task = Task.Run(() => watcher.WaitForChanged(WatcherChangeTypes.Deleted, timeoutInMilliseconds));
// we must not start deleting before the watcher is running
while (task.Status != TaskStatus.Running)
{
Thread.Sleep(100);
}
try
{
Directory.Delete(directory, true);
}
catch
{
return false;
}
return !task.Result.TimedOut;
}
请注意,获取task.Result
将阻塞线程,直到任务完成为止,从而使该线程的CPU负载保持空闲状态。所以这就是同步的地方。
答案 2 :(得分:-2)
听起来像我的竞争条件。不确定为什么 - 你没有提供足够的细节 - 但你可以做的是将所有内容包装在lock()语句中,看看问题是否已经消失。当然,这不是生产就绪的解决方案,它只是一种快速检查方式。如果它确实是竞争条件 - 您需要重新考虑重写文件夹的方法。可能是创造" GUID"文件夹和完成后 - 使用最新的GUID更新DB以指向最新的文件夹?..