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

时间:2016-02-26 21:09:30

标签: c# .net

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

  1. 打开文件(如果存在)
  2. 否则创建新文件
  3. 操作结果应为:

    1. 打开文件流
    2. 表明发生了什么行动
    3. 到目前为止,我已尝试使用System.IO.File.Open() System.IO.FileMode.OpenOrCreate,但该方法没有提供区分文件是打开还是创建的机制。

2 个答案:

答案 0 :(得分:2)

到目前为止,我已经发布了我最好的解决方案,以回答我自己的问题,但我希望能找到一个更好的解决方案,因为我的解决方案并不完全正确。该解决方案是原子的,但未能严格确定针对特定极端情况采取的操作。

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

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

编辑正如@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;

答案 1 :(得分:0)

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

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

但是我担心这只会让你的同步问题在未来发展得更进一步。