在Windows上以事务方式写入文件更改

关于如何确保表示特定状态的文件以一致的方式写入的官方微软build议是将其写入临时文件并replace文件。

但是如果我们假定更高级别的任务 – 更改文件中表示的状态 – 则会变得更加棘手。

要更改文件中的状态,您需要从文件中读取状态,进行更改并将其写回。 虽然我们可能会考虑使用ReplaceFile函数来覆盖“写入”部分,但是自从我们读取文件之后文件可能已经被更改的事实不是。

换句话说,在ReplaceFile调用之前,我们可能需要检查文件是否仍然相同并且没有更新。 如果我们在这里谈论文本编辑 – 在通话之前进行修改时间检查就足够了。 但是,如果我们想要一些更强大的东西 – 我们应该承认修改时间检查后,但在调用之前文件更改的可能性。

天真的做法是实现一个CompareAndReplaceFile调用,它将locking原始文件,检查它是否是同一个文件,然后复制ReplaceFile所做的。 这不仅仅是一个简单的解决scheme(系统函数的复制粘贴逻辑不是一个好的做法),也意味着更长的locking时间。

例如,在Linux上,通过使用fcntl(2)的( FD_SETLEASE )文件租用,在其他人打开文件进行写入之前有可能中止操作,在重命名(2)之前 ,是primefaces的,并不打开一个文件,所以你可以通过它保留一个租约。

有没有办法在Windows上实现事务性文件更改,除了上面讨论的一个黑客解决scheme?

当您使用CreateFile打开文件时,您将设置共享模式。 如果不指定FILE_SHARE_WRITE ,则在关闭句柄之前(如果文件已经打开以进行写入访问,则尝试失败并出现共享冲突),则无人可以打开该文件进行写入访问。

由于ReplaceFile使用GENERIC_READ, DELETE, and SYNCHRONIZE标志以及FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE共享模式下,您可以使用共享模式FILE_SHARE_READ | FILE_SHARE_DELETE FILE_SHARE_READ | FILE_SHARE_DELETE并保持打开,直到调用ReplaceFile ,从而排除竞争条件。

如果你在内存中保存内容(文本编辑器的情况下),那么当你保存你会:

  • 使用GENERIC_WRITE和共享模式FILE_SHARE_READ | FILE_SHARE_DELETE重新打开文件 FILE_SHARE_READ | FILE_SHARE_DELETE (如果原始句柄包含FILE_SHARE_WRITE ,不包括GENERIC_WRITE ,或者在读入工作缓冲区后关闭)
  • 执行修改时间检查。
  • 将更改写入新的临时文件。
  • 调用ReplaceFile
  • 关闭替换文件的句柄。

如果第一步失败并出现共享冲突,或者第二步揭示了另一个变更,则需要阅读更改后的内容,进行三向合并,然后启动流程。

通常一个文件首先被锁定(WinFile上的LockFile或POSIX OS-es上的Flock),然后被替换/更新。 您可以获得一个共享(只读)锁或一个独占(读/写)锁(或两者)(首先获取一个共享锁读取,并且只有在准备好替换/覆盖文件时,排他锁之后)。

你甚至可以检查文件的修改时间戳,并且如果所有者进程因任何原因没有释放锁,则覆盖/忽略该锁。