如何避免 File.Exists/File.Delete 或 Directory.Exists/Directory.Delete 中的竞争

How to avoid race in File.Exists/File.Delete or Directory.Exists/Directory.Delete

if (Directory.Exists(dir))
    Directory.Delete(dir, true);

以上代码检查目录是否存在,存在则删除。在存在检查和删除之间,有可能添加或删除了目录。

除了调用 .Delete 并丢弃异常之外,是否有适当的方法来防止这种竞争情况?

编辑:

避免与异常处理竞争条件的原因是因为exceptions should not be used for control flow.

理想的解决方案是某种文件系统锁?

你能做的最好的事情就是混合使用 Directory.Exists 和异常处理

if (Directory.Exists(dir))
{
    try 
    {
        Directory.Delete(dir, true);
    }
    catch (DirectoryNotFoundException e)
    {
        // do nothing, it means it doesn't exist
    }
}

为什么不试试看呢?因为不应该使用异常来处理应用程序流。您尝试使用 if 语句避免异常,但在极端情况下它发生了,您可以处理它。

您的代码是唯一可以删除或添加此类文件夹的代码吗?
如果是这样,为什么不直接将关键部分添加到代码中,例如 object 单例和 lock 构造?

如果您想避免删除一个已经删除的文件夹,您可以为它添加一个进程handle,这样其他进程就无法破坏它。

如果您想避免此代码不会删除所需目录的情况,我想不出一个好的解决方案。想象一下这种情况:您的代码运行,并且在它完成后,有人重新创建了文件夹。

在这种情况下,为什么不只捕获特定的异常呢?这是 .NET 中处理意外情况的自然方式。有一个lot of situations you can't predict:

Exceptions

IOException
A file with the same name and location specified by path exists. -or-
The directory specified by path is read-only, or recursive is false and path is not an empty directory. -or-
The directory is the application's current working directory. -or-
The directory contains a read-only file. -or-
The directory is being used by another process.

UnauthorizedAccessException
The caller does not have the required permission.

ArgumentException
path is a zero-length string, contains only white space, or contains one or more invalid characters. You can query for invalid characters by using the GetInvalidPathChars method.

ArgumentNullException path is null.

PathTooLongException
The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters and file names must be less than 260 characters.

DirectoryNotFoundException
path does not exist or could not be found. -or-
The specified path is invalid (for example, it is on an unmapped drive).

如果想要的最终结果是确保目录dir不存在,不管它是否存在,那么你应该调用Directory.Delete 并捕获它可能抛出的任何异常,而无需检查目录是否存在。然后你应该检查目录是否存在,看看你是否可以继续,或者你的操作是否由于其他原因失败:

try {
    Directory.Delete(dir, true);
} catch {
    // Ignore any exceptions
}
if (Directory.Exists(dir)) {
    // The above has failed to delete the directory.
    // This is the situation to which your program probably wants to react.
}