在Python中以独占方式打开文件的最佳方法是什么?
怎么优雅地解决这个问题呢:
- 打开一个文件进行读取,但前提是这个文件没有被打开用于写入
- 打开一个文件进行写入,但前提是这个文件没有被打开用于读取或写入
内置的函数是这样工作的:
>>> 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 个回答
编辑:我自己解决了这个问题! 我通过使用目录存在性和时间戳作为锁定机制来实现的!在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上不会引发任何异常。
有什么建议吗?谢谢!
附言:我知道这不是正确的方法,但我缺乏其他选择。请不要因为这个降低我的声望。我已经努力寻找解决方案。这里怎么私信用户?还有唉,我为什么不能?
这个解决方案应该可以在同一个程序里工作(就像上面的例子那样),也可以在其他程序打开文件的时候使用。
如果你说的“其他程序”是指“任何程序”(也就是不是你的程序),在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命令,也无法读取这个文件,直到你释放锁。
我觉得没有一种完全适合所有平台的方法。在Unix系统上,可以使用fcntl模块来实现这个功能。不过在Windows系统上(我猜你是在用Windows,因为路径是这样的),你需要用到win32file模块。
幸运的是,有一个可移植的实现方式,叫做portalocker,它使用了适合不同平台的方法,具体可以参考Python的食谱。
使用这个库时,首先打开文件,然后调用:
portalocker.lock(file, flags)
这里的flags可以用portalocker.LOCK_EX表示独占写入权限,或者用LOCK_SH表示共享读取权限。