在Python中打开文件进行独占访问的最佳方法是什么?

mar*_*r10 41 python locking file

解决这个问题最优雅的方法是什么:

  • 打开一个文件进行阅读,但前提是它尚未打开才能写入
  • 打开文件进行写入,但前提是它尚未打开进行读写

内置函数的工作方式如下

>>> 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()
Run Code Online (Sandbox Code Playgroud)

scr.txt现在包含'111'.

>>> file2.close()
Run Code Online (Sandbox Code Playgroud)

scr.txt被覆盖,现在包含'222'(在Windows上,Python 2.4).

解决方案应该在同一个进程中工作(如上例所示)以及另一个进程打开文件时.
如果崩溃程序不能保持锁定打开,则是首选.

Bri*_*ian 24

我不认为有完全跨平台的方式.在unix上,fcntl模块将为您执行此操作.但是在Windows上(我假设你是路径),你需要使用win32file模块.

幸运的是,有一个可移植的实现(portalocker)在python cookbook上使用平台适当的方法.

要使用它,请打开该文件,然后调用:

portalocker.lock(file, flags)
Run Code Online (Sandbox Code Playgroud)

其中flags是portalocker.LOCK_EX用于独占写访问,或LOCK_SH用于共享,读访问.

  • 注意到PyPi上有更新的版本:http://pypi.python.org/pypi/portalocker (3认同)
  • 这使用Mark Hammond的"python for windows"扩展:http://starship.python.net/crew/mhammond/.如果它们不可用,您将在"import win32con"中收到错误消息. (2认同)
  • @WimCoenen Anatoly Techtonik通过[porting](http://code.activestate.com/recipes/65203/#c10)portalocker删除了对该模块的依赖. (2认同)
  • Portalocker有很多奇怪之处.它不会像你想象的那样工作.例如,如果在文件打开模式下使用`portalocker.Lock()`打开文件,包括`'w'`,则在测试锁之前将截断该文件.锁定是一个难题.Portalocker并没有真正解决它.在依赖门户网站代码之前,请务必先阅读它们. (2认同)

Fed*_*oni 11

解决方案应该在同一个进程中工作(如上例所示)以及另一个进程打开文件时.

如果通过"另一个进程",你的意思是"无论什么进程"(即不是你的程序),在Linux中,没有办法只在系统调用(fcntl和朋友)上实现这一点.你想要的是强制锁定,并且获取它的Linux方式更复杂:

使用mand选项重新安装包含文件的分区:

# mount -o remount,mand /dev/hdXY
Run Code Online (Sandbox Code Playgroud)

为您的文件设置sgid标志:

# chmod g-x,g+s yourfile
Run Code Online (Sandbox Code Playgroud)

在Python代码中,获取该文件的独占锁:

fcntl.flock(fd, fcntl.LOCK_EX)
Run Code Online (Sandbox Code Playgroud)

现在,即使你释放锁定,cat也无法读取文件.


akr*_*ger 6

编辑:我自己解决了!通过使用目录存在和年龄作为锁定机制!按文件锁定仅在 Windows 上是安全的(因为 Linux 会静默覆盖),但按目录锁定在 Linux 和 Windows 上都可以完美运行。请参阅我的 GIT,我为此创建了一个易于使用的类“lockbydir.DLock”

https://github.com/drandreaskrueger/lockbydir

在自述文件的底部,您会找到 3 个 GITplayer,您可以在其中看到代码示例在浏览器中实时执行!很酷,不是吗?:-)

感谢您的关注


这是我原来的问题:

我想回答 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 在不同的文件系统上,则操作可能会在某些 Unix 风格上失败。如果成功,重命名将是一个原子操作(这是 POSIX 要求)。在 Windows 上,如果 dst 已经存在,将引发 OSError ( https://docs.python.org/2/library/os.html#os.rename )

无声替换是问题所在。在 Linux 上。“如果 dst 已经存在,将引发 OSError”对我来说非常有用。但遗憾的是,仅在 Windows 上。

我猜 parity3 的例子大部分时间仍然有效,因为他的 if 条件

if not os.path.exists(lock_filename):
    try:
        os.rename(tmp_filename,lock_filename)
Run Code Online (Sandbox Code Playgroud)

但是,整个事情不再是原子的。

因为 if 条件可能在两个并行进程中为真,然后两者都会重命名,但只有一个会赢得重命名竞赛。并没有引发异常(在 Linux 中)。

有什么建议?谢谢!

PS:我知道这不是正确的方法,但我缺乏替代方法。请不要用降低我的声誉来惩罚我。我环顾四周,自己解决了这个问题。如何在这里PM用户?而MEH我为什么不能?


Jos*_*eia 5

我更喜欢使用filelock,这是一个跨平台的 Python 库,几乎不需要任何额外的代码。以下是如何使用它的示例:

from filelock import FileLock

lockfile = r"c:\scr.txt"
lock = FileLock(lockfile + ".lock")
with lock:
    file = open(path, "w")
    file.write("111")
    file.close()
Run Code Online (Sandbox Code Playgroud)

块中的任何代码with lock:都是线程安全的,这意味着它将在另一个进程访问该文件之前完成。

  • 可以扩展一下吗 如果另一个程序当前正在写入(因此它已设置锁定)将会发生什么。这个会等待并重试吗?多久重试一次? (2认同)