我如何以原子方式打开或创建文件并知道发生了哪个操作

本文关键字:文件 发生了 操作 创建 方式打 | 更新日期: 2023-09-27 18:36:37

给定一个文件路径,我希望以原子方式执行两个操作中的任何一个,并能够确定实际发生了两个操作中的哪一个:

  1. 打开文件(如果存在)
  2. 否则创建一个新文件

操作的结果应为:

  1. 打开的文件流
  2. 指示发生了什么操作

到目前为止,我尝试使用System.IO.FileMode.OpenOrCreate System.IO.File.Open(),但该方法没有提供区分文件是打开还是创建的机制。

我如何以原子方式打开或创建文件并知道发生了哪个操作

到目前为止,

我正在发布我最好的尝试解决方案来回答我自己的问题,但我希望会出现更好的解决方案,因为我的解决方案并不能完全解决问题。 该解决方案是原子的,但无法严格识别针对特定极端情况采取了哪些操作。

我的解决方案是将任何零字节文件视为等效于新创建的文件。 当然,这是一个错误的等价,因为完全有可能打开现有的零字节文件。

然而,出于我的应用程序(以及许多其他应用程序)的目的,将两者视为等效是切实可行的。 在我的应用程序中,我打算打开包含现有数据的文件,或者将数据发布到新文件。 从这个角度来看,如果数据不存在,该文件也可能不存在。 但是,对于将零字节文件视为重要的应用程序,该解决方案是不够的。

编辑 正如@Martin Soles所指出的,其他进程可能会在创建文件后写入文件。 若要防止此争用条件,必须使用 System.IO.FileShare.Read 打开文件。

实现:

public FileStream OpenOrCreateFile(string path, FileAccess fileAccess, out bool isNewFileForAllPracticalPurposes)
{
    var fs = File.Open(path, FileMode.OpenOrCreate, fileAccess, FileShare.Read);
    isNewFileForAllPracticalPurposes = (fs.Length == 0); // Consider any zero-byte file to be a "new" file.
    return fs;
}

用法:

bool isNewFileForAllPracticalPurposes;
OpenOrCreateFile(@"C:'myFile.txt", FileAccess.ReadWrite, out isNewFileForAllPracticalPurposes);
// Alert! This assignment lies, strictly speaking.
bool aNewFileWasCreated = isNewFileForAllPracticalPurposes;

如果你愿意更接近铁,你可以使用 Win32 CreateFile API 来满足你的原子性要求。使用 OPEN_ALWAYS 作为 dwCreationDisposition 值。 如果成功打开文件,则可以调用 GetLastError 来检查ERROR_ALREADY_EXISTS值,以确定是创建该文件还是打开了现有文件。 我知道在成功操作后调用 GetLastError 似乎很奇怪,但这是ERROR_ALREADY_EXISTS常见的 Win32 习语。

获得文件句柄后,可以使用 .Net SafeFileHandle 类将其包装并将其附加到 FileStream。

但我担心这只会将您的同步问题推得更远一点。

相关文章: