关于如何确保表示特定状态的文件以一致的方式写入的官方微软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),然后被替换/更新。 您可以获得一个共享(只读)锁或一个独占(读/写)锁(或两者)(首先获取一个共享锁读取,并且只有在准备好替换/覆盖文件时,排他锁之后)。
你甚至可以检查文件的修改时间戳,并且如果所有者进程因任何原因没有释放锁,则覆盖/忽略该锁。