给定文件路径,我希望以原子方式执行两个动作中的任何一个,并且能够确定实际发生的两个动作中的哪一个:
操作结果应为:
到目前为止,我已尝试使用System.IO.File.Open()
System.IO.FileMode.OpenOrCreate
,但该方法没有提供区分文件是打开还是创建的机制。
答案 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。
但是我担心这只会让你的同步问题在未来发展得更进一步。