关于如何确保代表特定状态的文件以一致的方式编写的official microsoft recommendation是将其写入临时文件并ReplaceFile。
但是,如果我们假设更高级别的任务 - 对文件中所表示的状态的更改 - 它会变得更有问题。
要更改文件中的状态,您需要从文件中读取状态,进行更改并将其写回。虽然ReplaceFile函数可能会考虑“写入”部分,但自从我们阅读之后文件可能已被更改的事实并非如此。
换句话说,我们可能需要在ReplaceFile调用之前检查文件是否仍然相同且未更新。如果我们在这里谈论文本编辑器 - 在调用之前进行修改时间检查就足够了。但如果我们想要更强大的东西 - 我们应该承认 修改时间检查后但在通话之前文件更改的可能性。
天真的方法是实现一个 CompareAndReplaceFile 调用,它将锁定原始文件,检查它是否是同一个文件,然后复制ReplaceFile所做的事情。这不仅仅是一个有点hacky的解决方案(系统功能的复制粘贴逻辑不是一个好的做法),但它也意味着更长的锁定时间。
例如,在Linux上,使用fcntl(2)( FD_SETLEASE )文件租赁可以实现同样的效果,一旦其他人打开文件就有可能中止您的操作用于写入rename(2)之前,这是原子的并且不打开文件,所以你可以通过它保持租约。
除了上面讨论的hacky解决方案之外,还有办法在Windows上实现事务性文件更改吗?
答案 0 :(得分:1)
使用CreateFile
打开文件时,设置共享模式。如果您没有指定FILE_SHARE_WRITE
,则在关闭句柄之前,没有人可以打开文件进行写访问(如果文件已经打开以进行写访问,则您的尝试会因共享冲突而失败)。
由于ReplaceFile
使用GENERIC_READ, DELETE, and SYNCHRONIZE
标记和FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
共享模式执行操作,您可以使用FILE_SHARE_READ | FILE_SHARE_DELETE
的共享模式打开您的写入句柄,并保持打开状态直到在致电ReplaceFile
之后,从而排除了竞争条件。
如果您将内容保留在内存中(文本编辑器案例),那么在保存时您将:
GENERIC_WRITE
和共享模式FILE_SHARE_READ | FILE_SHARE_DELETE
重新打开文件(如果原始句柄包含FILE_SHARE_WRITE
,未包含GENERIC_WRITE
,或者在阅读后已关闭进入工作缓冲区)ReplaceFile
如果第一步因共享违规而失败,或第二步显示另一项更改,则您需要阅读更改的内容,进行三向合并,然后开始此过程。
答案 1 :(得分:0)
通常在替换/更新文件之前首先锁定文件(WinF上的LockFile或POSIX OS-es的flock)。您可以获得共享(只读)锁,或独占(读/写)锁,或两者兼而有(首先获取共享锁以进行读取,并且只有在准备替换/覆盖文件时才进行独占锁)。
如果所有者进程因任何原因未释放锁,您甚至可以检查文件的修改时间戳并覆盖/忽略锁。