我需要将文件从一个位置复制到另一个位置,如果文件已存在于目的地(无覆盖),我需要抛出异常(或至少以某种方式识别)。
我可以先使用os.path.exists()进行检查,但是在检查和复制之间的少量时间内无法创建文件是非常重要的。
是否有内置的方法可以做到这一点,还是有办法将动作定义为原子?
答案 0 :(得分:13)
没有办法做到这一点;文件复制操作绝不是原子的,也没办法制作它们。
但您可以使用随机的临时名称编写该文件,然后重命名它。重命名操作必须是原子的。 如果文件已存在,则重命名将失败,您将收到错误。
[EDIT2] rename()
只有在同一文件系统中执行时才是原子的。安全的方法是在与目标文件相同的文件夹中创建新文件。
[编辑] 关于重命名是否始终是原子的以及重写行为,有很多讨论。所以我挖出了一些资源。
在Linux上,如果目标存在且源和目标都是文件,则会以静默方式覆盖目标(man page)。所以我错了。
但是rename(2)
仍然保证原始文件或新文件在出现问题时仍然有效,因此操作是原子的,因为它不能破坏数据。从某种意义上讲,它不是原子的,它可以阻止两个进程同时进行相同的重命名,并且可以预测结果。一个人会赢,但你不知道哪个。
在Windows上,如果另一个进程当前正在编写该文件,如果您尝试将其打开以进行编写,则会出现错误,因此这对Windows来说是一个优势。
如果您的计算机在将操作写入磁盘时崩溃,则文件系统的实现将决定损坏的数据量。应用程序可以执行 nothing 。所以不要再抱怨了: - )
也没有其他方法可以更好地工作,甚至也不如此方法。
您可以使用文件锁定。但这只会使一切变得更加复杂并且不会产生额外的优势(除了更复杂,有些人确实认为这是一个巨大的优势)。当你的文件在网络驱动器上时,你会添加许多漂亮的角落案例。
您可以将open(2)
与模式O_CREAT
一起使用,如果该文件已存在,将导致该函数失败。但这不会阻止第二个进程删除文件并编写自己的副本。
或者你可以创建一个锁目录,因为创建目录也必须是原子的。但这也不会给你带来太大的收益。你必须亲自编写锁定代码并绝对做到,100%确定你真的,真的总是在发生灾难时删除锁定目录 - 你不能这样做。
答案 1 :(得分:9)
实际上是一种原子安全的方式,如果所有演员以同样的方式执行。它是lock-free whack-a-mole algorithm的改编版,并非完全无关紧要,所以请随意将“否”作为一般答案;)
<target>.<UUID>.tmp
。<target>-<UUID>.mole.tmp
。<target>-*.mole.tmp
。
<target>
。 (不要担心,如果它消失了,只需跳回到第5步。)你已经完成了!
想象一下,每个候选源文件都是一个鼹鼠。在检查没有其他鼹鼠完全出现之前,中途停止并将任何竞争的鼹鼠重新打入地面。如果你在头脑中经历这一过程,你应该看到只有一颗痣会完全消失。为了防止这个系统来自livelocking,我们添加了一个可以敲打哪个痣的总排序。巴姆! 博士论文 lock-free algorithm。
†步骤4可能看起来不必要 - 为什么不首先使用该名称?但是,另一个进程可能会在步骤5中“采用”您的 mole 文件,并使其成为步骤7中的获胜者,因此您仍然没有写出内容非常重要!在同一文件系统上重命名是原子的,因此第4步是安全的。