在Python中以独占方式打开文件的最佳方法是什么?

56 投票
7 回答
74665 浏览
提问于 2025-04-11 09:26

怎么优雅地解决这个问题呢:

  • 打开一个文件进行读取,但前提是这个文件没有被打开用于写入
  • 打开一个文件进行写入,但前提是这个文件没有被打开用于读取或写入

内置的函数是这样工作的:

>>> path = r"c:\scr.txt"
>>> file1 = open(path, "w")
>>> print file1
<open file 'c:\scr.txt', mode 'w' at 0x019F88D8>
>>> file2 = open(path, "w")
>>> print file2
<open file 'c:\scr.txt', mode 'w' at 0x02332188>
>>> file1.write("111")
>>> file2.write("222")
>>> file1.close()

现在,scr.txt 文件里包含 '111'。

>>> file2.close()

scr.txt 被覆盖了,现在里面是 '222'(在 Windows 系统上,使用 Python 2.4)。

这个解决方案应该在同一个程序内部工作(就像上面的例子那样),同时也要能处理其他程序已经打开这个文件的情况。
如果程序崩溃的话,最好不要让它一直保持锁定状态。

7 个回答

11

编辑:我自己解决了这个问题! 我通过使用目录存在性和时间戳作为锁定机制来实现的!在Windows上通过文件锁定是安全的(因为Linux会默默覆盖),但通过目录锁定在Linux和Windows上都能完美工作。你可以查看我的GIT,我创建了一个易于使用的类'lockbydir.DLock'来实现这个功能:

https://github.com/drandreaskrueger/lockbydir

在自述文件的底部,你会找到3个GIT示例,可以在浏览器中实时查看代码示例的执行!相当酷,对吧?:-)

感谢你的关注


这是我最初的问题:

我想回复parity3(https://meta.stackoverflow.com/users/1454536/parity3),但我既不能直接评论(“你必须有50的声望才能评论”),也看不到任何直接联系他/她的方法。你有什么建议让我能联系到他吗?

我的问题:

我实现了类似于parity3在这里建议的内容作为回答:https://stackoverflow.com/a/21444311/3693375(“假设你的Python解释器,以及...”)

而且它在Windows上运行得非常好。(我用它来实现一个跨独立启动进程的锁定机制。https://github.com/drandreaskrueger/lockbyfile

但与parity3所说的不同,它在Linux上并不一样:

os.rename(src, dst)

将文件或目录src重命名为dst。... 在Unix上,如果dst存在并且是一个文件,如果用户有权限,它将被默默替换。如果src和dst在不同的文件系统上,操作可能会失败。如果成功,重命名将是一个原子操作(这是POSIX的要求)。在Windows上,如果dst已经存在,将会引发OSError。

默默替换就是问题所在。在Linux上。 “如果dst已经存在,将引发OSError”对我来说很好,但可惜只有在Windows上。

我猜parity3的例子大多数时候仍然有效,因为他的if条件

if not os.path.exists(lock_filename):
    try:
        os.rename(tmp_filename,lock_filename)

但这样整个过程就不再是原子操作了。

因为if条件可能在两个并行进程中都为真,然后两个都会重命名,但只有一个会赢得重命名的竞争。而且在Linux上不会引发任何异常。

有什么建议吗?谢谢!

附言:我知道这不是正确的方法,但我缺乏其他选择。请不要因为这个降低我的声望。我已经努力寻找解决方案。这里怎么私信用户?还有,我为什么不能?

15

这个解决方案应该可以在同一个程序里工作(就像上面的例子那样),也可以在其他程序打开文件的时候使用。

如果你说的“其他程序”是指“任何程序”(也就是不是你的程序),在Linux系统中,单靠系统调用(比如fcntl等)是无法做到这一点的。你想要的是一种叫做强制锁定的机制,而在Linux中实现这个机制的方法稍微复杂一些:

首先,你需要用mand选项重新挂载包含你文件的分区:

# mount -o remount,mand /dev/hdXY

然后,为你的文件设置sgid标志:

# chmod g-x,g+s yourfile

接下来,在你的Python代码中,获取这个文件的独占锁:

fcntl.flock(fd, fcntl.LOCK_EX)

这样,即使是用cat命令,也无法读取这个文件,直到你释放锁。

31

我觉得没有一种完全适合所有平台的方法。在Unix系统上,可以使用fcntl模块来实现这个功能。不过在Windows系统上(我猜你是在用Windows,因为路径是这样的),你需要用到win32file模块。

幸运的是,有一个可移植的实现方式,叫做portalocker,它使用了适合不同平台的方法,具体可以参考Python的食谱。

使用这个库时,首先打开文件,然后调用:

portalocker.lock(file, flags)

这里的flags可以用portalocker.LOCK_EX表示独占写入权限,或者用LOCK_SH表示共享读取权限。

撰写回答