安official microsoft recommendation关于如何确保以一致的方式写入表示特定状态的文件的方法是将其写入临时文件和ReplaceFile
但如果我们承担更高级别的任务 - 更改文件中表示的状态 - 它会变得有点问题。
要更改文件中的状态,您需要从文件中读取状态,进行更改并将其写回。虽然我们可以考虑将“写作”部分包含在 ReplaceFile 中。函数,事实上文件在我们读取后可能已经被更改,但事实并非如此。
换句话说,我们可能需要在 ReplaceFile 之前检查文件是否仍然相同并且没有更新。称呼。如果我们在这里讨论文本编辑器——在调用之前进行修改时间检查就足够了。但如果我们想要更强大的东西——我们应该承认 在修改时间检查之后但在调用之前更改文件的可能性。
天真的方法是实现一个CompareAndReplaceFile 调用,这将锁定原始文件,检查它是否是同一个文件,然后复制ReplaceFile 的内容。做。这不仅是一个有点 hacky 的解决方案(系统功能的复制粘贴逻辑不是一个好的做法),而且它还意味着更长的锁定期。
例如,在 Linux 上,使用 fcntl(2) 可以达到同样的效果。的 (FD_SETLEASE) 文件租赁有机会在其他人打开文件进行写入时中止您的操作,在 rename(2) 之前,它是原子的,不会打开文件,因此您可以通过它保持租约。
除了上面讨论的 hacky 解决方案之外,是否有其他方法可以在 Windows 上实现事务性文件更改?
最佳答案
当您使用 CreateFile
打开文件时,您设置了共享模式。如果您不指定 FILE_SHARE_WRITE
,则在您关闭文件句柄之前,没有人可以打开文件进行写访问(如果文件已经打开进行写访问,您的尝试将因共享冲突而失败) .
因为 ReplaceFile
使用 GENERIC_READ、DELETE 和 SYNCHRONIZE
标志和 FILE_SHARE_READ | 执行操作文件共享写入 | FILE_SHARE_DELETE
共享模式,您可以使用 FILE_SHARE_READ | 共享模式打开您的写句柄FILE_SHARE_DELETE
并在调用 ReplaceFile
之前保持打开状态,从而排除竞争条件。
如果您将内容保存在内存中(文本编辑器的情况),那么在保存时您将:
- 使用
GENERIC_WRITE
和共享模式FILE_SHARE_READ | 重新打开文件 | FILE_SHARE_DELETE
(如果原始句柄包含FILE_SHARE_WRITE
,不包含GENERIC_WRITE
,或者在读入工作缓冲区后已关闭) - 执行修改时间检查。
- 将更改写入新的临时文件。
- 调用
ReplaceFile
- 关闭被替换文件的句柄。
如果第一步因共享违规而失败,或者第二步显示另一个更改,您将需要阅读更改的内容,进行三向合并,然后重新开始该过程。
关于windows - 在 Windows 上以事务方式写入文件更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32532399/