我需要将文件从一个位置复制到另一个位置,并且如果该文件已存在于目标(不覆盖),我需要引发异常(或至少以某种方式识别)。安全的原子文件复制操作
我可以先用os.path.exists()检查,但是在检查和复制之间不能在少量时间内创建文件是非常重要的。
有没有这样做的内置方式,或者有没有办法将动作定义为原子?
我需要将文件从一个位置复制到另一个位置,并且如果该文件已存在于目标(不覆盖),我需要引发异常(或至少以某种方式识别)。安全的原子文件复制操作
我可以先用os.path.exists()检查,但是在检查和复制之间不能在少量时间内创建文件是非常重要的。
有没有这样做的内置方式,或者有没有办法将动作定义为原子?
有其实办法做到这一点,原子和安全,提供的所有演员都以相同的方式。这是lock-free whack-a-mole algorithm的适应,而不是完全微不足道的,可以随意去与“无”为一般的答案;)
<target>.<UUID>.tmp
。<target>-<UUID>.mole.tmp
。<target>-*.mole.tmp
。
<target>
。 (不要担心,如果它消失了,只需跳回步骤5.)你完成了!
试想每个候选源文件即将出洞的痣。在中途停下来之前,它会暂停并将任何竞争性的痣拨回地面,然后检查是否没有其他痣完全出现。如果你在脑中运行这个过程,你应该看到只有一个痣会使它完全消失。为了防止这个系统从livelocking,我们添加一个总的顺序,哪个鼹鼠可以击中哪个。巴姆! A
博士论文
lock-free algorithm。
†第4步可能看起来不必要—为什么不只是在第一个地方使用该名称?但是,另一个过程可能会在步骤5中“采用”您的
文件,并使其成为第7步中的赢家,所以非常重要的是您不会写出内容!在同一个文件系统上重命名是原子的,所以第4步是安全的。
这是一个漂亮的算法。我不认为我会这么做IRL,但这种方法很巧妙。 – Rory 2015-05-12 18:26:57
这是否依赖uuid产生增量值? – coolfeature 2016-10-03 13:33:45
@coolfeature不,订购仅用于确保最终选择赢家。 – 2016-10-03 15:46:02
有没有办法做到这一点;文件复制操作从来不是原子的,也没有办法使它们成为可能。
但是你可以将该文件写入一个随机的临时名称,然后重命名为它。重命名操作必须是原子操作。 如果文件已经存在,重命名将会失败,并且会出现错误。
[编辑2]rename()
只有在同一文件系统中执行时才是原子。安全的方法是在与目的地相同的文件夹中创建新文件。
[编辑]有很多讨论重新命名是否总是原子性和关于覆盖行为。所以我挖掘了一些资源。
在Linux上,如果目标存在,并且源和目标都是文件,则目标将被自动覆盖(man page)。所以我错了。
但是rename(2)
仍然保证原始文件或新文件在出现问题时保持有效,所以操作是原子的,因为它不会破坏数据。从某种意义上说,它不是原子的,它可以防止两个进程同时进行相同的重命名,并且可以预测结果。一个会赢,但你不知道哪个。
在Windows上,如果另一个进程当前正在写入该文件,如果您尝试打开该文件以进行写入,则会出现错误,因此Windows的优势如下。
如果您的计算机在操作写入磁盘时崩溃,则文件系统的实现将决定有多少数据被损坏。有没有什么一个应用程序可以做到这一点。所以停止呜呜声已经:-)
也没有其他的方法,更好地工作,甚至就像这一个。
您可以使用文件锁定代替。但是这样做会使一切变得更加复杂,并且不会产生额外的优势(除了由于某些原因,一些人认为这是一个巨大的优势更复杂)。当你的文件在网络驱动器上时,你会添加很多不错的角落案例。
如果文件已经存在,您可以使用open(2)
的模式O_CREAT
,这将导致函数失败。但这并不妨碍第二个进程删除该文件并编写自己的副本。
或者你可以创建一个锁目录,因为创建目录也必须是原子的。但那也不会让你买得太多。你必须自己编写锁代码并绝对保证,100%确定你真的,真的会在发生灾难的情况下删除锁目录 - 你不能。
它只是创建需要是原子的目的地,但也有源内容,如阅读,只代表一个时间点? – 2012-07-23 14:57:49
只是创造。我正在编写一个程序,将区域文件复制到/ tmp,进行必要的更改,然后在最后复制它。我只需要确定两个人是否同时尝试和编辑,其中一个人不会失去他们的变化。 – Rory 2012-07-23 15:10:55
请注意,如果源和目标位于同一个文件系统上,那么'rename()'只是原子 - 所以您可能想要在目标目录中创建临时文件,而不是在'/ tmp'中。 – 2012-07-23 15:26:52