我如何以原子方式打开或创建文件并知道发生了哪个操作
本文关键字:文件 发生了 操作 创建 方式打 | 更新日期: 2023-09-27 18:36:37
给定一个文件路径,我希望以原子方式执行两个操作中的任何一个,并能够确定实际发生了两个操作中的哪一个:
- 打开文件(如果存在)
- 否则创建一个新文件
操作的结果应为:
- 打开的文件流
- 指示发生了什么操作
到目前为止,我尝试使用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。
但我担心这只会将您的同步问题推得更远一点。