使用 Directory.Delete() 和 Directory.CreateDirectory() 覆盖文件夹

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

在我的 WebApi 操作方法中,我想 create/over-write 使用此代码的文件夹:

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() returns true,其他时间 false。第二次检查目录(数字 2 所在的位置)时也会发生同样的情况。

备注

问题

或者一般来说:

更新:

对我来说听起来像是竞争条件。不知道为什么 - 你没有提供足够的细节 - 但你可以做的是将所有内容包装在 lock() 语句中,看看问题是否消失了。可以肯定的是,这不是生产就绪的解决方案,它只是一种快速检查的方法。如果它确实是一个竞争条件 - 你需要重新考虑你重写文件夹的方法。可能会创建 "GUID" 文件夹,完成后 - 使用最新的 GUID 更新数据库以指向最新的文件夹?..

许多文件系统操作在某些文件系统上不同步(在 windows - NTFS 的情况下)。以 RemoveDirectory 调用为例(在某些时候由 Directory.DeleteDirectory 调用):

The RemoveDirectory function marks a directory for deletion on close. Therefore, the directory is not removed until the last handle to the directory is closed.

如您所见,它不会真正删除目录,直到它的所有句柄都关闭,但 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% 的时间都在做。

这是另一个代码,当 运行ning 在我的机器上确认肯定有可能 File.Exists 在 File.Delete 调用之后直接 return 为真:

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 文件。几次尝试后,抛出了 Confirmed 异常(虽然我没有创建或删除该文件,并且在抛出异常之后,它已经不存在于文件系统中)。所以竞争条件在多种情况下都是可能的,我的回答是正确的。

我自己写了一个使用Directory.Delete()同步文件夹删除的C#小方法。欢迎复制:

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负载空闲。这就是它同步的地方。