以下问题听起来可能有些漫长而复杂,但是实际上,这是一个在同一文件上运行三个进程的非常简单,通用和常见的问题。在下面的文本中,我试图通过一些说明性示例将问题分解为一组特殊要求。
任务序言
有一个名为索引的文本文件,其中包含一些元数据。
有一个应用程序( APP ),它可以理解文件格式并对其进行有意义的更改。
该文件存储在版本控制系统( VCS )下,该系统是其他用户对同一文件执行的更改的来源。
我们需要设计一个应用程序(APP),该应用程序可以在合理的文件中使用该文件,最好不要与VCS进行过多交互,因为假定使用VCS来保存大型项目,而索引文件只是其中的一小部分。用户可能希望在任何时候更新VCS,而无需考虑APP内正在进行的任何操作。在那种情况下,APP应该以防止任何可能的数据丢失的方式来适当地处理这种情况。
言辞
请注意,未指定VCS,它可能是perforce,git,svn,tarball,闪存驱动器或您最喜欢的基于WWII Morse的收音机和文本编辑器。
文本文件可以是二进制的,不会改变太多。但是考虑到VCS存储,它很容易被合并,因此文本/人类可读格式是最合适的。
此类事情的可能示例是:复杂的配置(AI行为树,游戏对象描述),资源列表,其他不希望手工编辑的,与当前项目有关,但历史很重要的事情。
请注意,除非您热衷于实现自己的版本控制系统,否则将大多数配置“外包”到某些基于客户端-服务器的外部解决方案将无法解决问题-您仍然必须将参考文件保留在版本控制系统中,对数据库中相关配置的匹配版本的引用。这意味着,您仍然有同样的问题,但是规模较小-文件中的单个文本行,而不是一打。
任务本身
处于真空中的通用APP可以分三个阶段进行索引:读取,修改,写入。读取阶段-读取和反序列化文件,修改-更改内存中状态,写入-序列化状态并写入文件。
此应用程序有三种通用工作流:
第一个工作流程是针对只读的“用户”,例如游戏客户端,它只读取一次数据而忘记了文件。
第二个工作流程是用于编辑应用程序。由于外部更新很少发生,并且用户不可能同时在几个编辑应用程序中编辑同一文件,因此可以合理地假设一个通用编辑应用程序只希望读取状态一次(尤其是在资源消耗操作),并且仅在外部更新的情况下才重新读取。
第三个工作流程是用于cli的自动化使用-构建服务器,脚本等。
考虑到这一点,合理地威胁分别读写和修改。让我们将一个仅进入读取阶段并准备一些信息的操作称为读取操作。 写操作将是一种修改读取操作的状态并将其写入磁盘的操作。
由于工作流一和二可能由不同的应用程序实例同时运行,因此允许多个读取操作同时运行也很合理。某些读取操作(如用于编辑应用程序的读取)可能要等到任何现有的写入操作完成后才能读取最新状态。其他读取操作(例如游戏客户端中的读取操作)可能希望读取当前状态,无论它是什么状态,而不会被阻塞。
另一方面,只有写操作能够检测到任何其他正在运行并中止的写操作才是合理的。写操作还应该检测对索引文件所做的任何外部更改并中止。基本原理-没有必要执行(和等待)任何工作,由于这些工作是基于可能的过时状态而被丢弃的,因此没有意义。
对于健壮的应用程序,应在应用程序的每个点上假设存在星系标度严重故障的可能性。在任何情况下,这种故障都不应使索引文件不一致。
要求
最佳答案
的Linux
读操作:
写操作:
如果收到租约的信号-中止和清除,则不重命名。 rename(2)没有提到它可能会被打断,而POSIX requires却是原子的,因此一旦我们了解它就可以了。
我知道有共享内存互斥锁和命名信号量(而不是应用程序实例之间进行协作的建议性锁定),但是我认为我们都同意,它们对于手头的任务来说不必要地复杂,并且有自己的问题。
视窗
读操作:
写操作:
在修改过程中,使用WaitForSingleObject(零超时)检查OVERLAPPED结构中的事件。如果存在索引事件-终止操作。否则-再次启动手表,检查我们是否仍是最新的;如果是,则-继续。
备注
关于c++ - 并发状态文件处理具有多个过程,超出了我们对Linux和Windows的控制范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32534319/