用Python锁定文件

Eva*_*ark 133 python file-locking

我需要锁定一个文件以便用Python编写.它将同时从多个Python进程访问.我在网上找到了一些解决方案,但大多数都失败了,因为它们通常只基于Unix或基于Windows.

Eva*_*ark 99

好吧,所以我最终得到了我在这里写的代码,在我的网站链接已经死了,在archive.org查看(也可以在GitHub上找到).我可以用以下方式使用它:

from filelock import FileLock

with FileLock("myfile.txt"):
    # work with the file as it is now locked
    print("Lock acquired.")
Run Code Online (Sandbox Code Playgroud)

  • 正如博客文章中的评论所指出的那样,这个解决方案并不"完美",因为程序可以以锁定保留的方式终止,你必须在文件之前手动删除锁定再次变得可访问.但是,除此之外,这仍然是一个很好的解决方案. (7认同)
  • @jweyrich Openstacks pylockfile现已弃用.建议使用[紧固件](https://pypi.python.org/pypi/fasteners)或[oslo.concurrency](http://docs.openstack.org/developer/oslo.concurrency). (6认同)
  • 它是只写锁还是读锁? (3认同)
  • 可以在这里找到Evan的FileLock的另一个改进版本:https://github.com/ilastik/lazyflow/blob/master/lazyflow/utility/fileLock.py (3认同)
  • OpenStack确实发布了自己的(好吧,Skip Montanaro的)实现 - **[pylockfile](https://github.com/openstack/pylockfile)** - 非常类似于之前评论中提到的那些,但仍值得一看. (3认同)
  • 我猜的另一个类似的实现:https://github.com/benediktschmitt/py-filelock (2认同)
  • 这些评论让人非常困惑,这是否仍然是推荐使用的包,或者我们是否应该使用更新的包。更新答案真的很好。 (2认同)

Joh*_*uhy 38

这里有一个跨平台文件锁定模块:Portalocker

虽然凯文说,但是如果可能的话,你想要立即从多个进程写入文件.

如果您可以将问题塞进数据库,则可以使用SQLite.它支持并发访问并处理自己的锁定.

  • +1 - SQLite几乎总是这种情况下的方式. (16认同)
  • Portalocker需要适用于Windows的Python Extensions. (2认同)
  • @naxa有一个变体,它仅依赖于msvcrt和ctypes,请参见http://roundup.hg.sourceforge.net/hgweb/roundup/roundup/file/tip/roundup/backends/portalocker.py (2认同)
  • SQLite支持并发访问吗? (2认同)

Tho*_*Lux 17

其他解决方案引用了许多外部代码库.如果您更愿意自己动手​​,这里有一些跨平台解决方案的代码,它使用Linux/DOS系统上的相应文件锁定工具.

try:
    # Posix based file locking (Linux, Ubuntu, MacOS, etc.)
    import fcntl, os
    def lock_file(f):
        fcntl.lockf(f, fcntl.LOCK_EX)
    def unlock_file(f):
        fcntl.lockf(f, fcntl.LOCK_UN)
except ModuleNotFoundError:
    # Windows file locking
    import msvcrt, os
    def file_size(f):
        return os.path.getsize( os.path.realpath(f.name) )
    def lock_file(f):
        msvcrt.locking(f.fileno(), msvcrt.LK_RLCK, file_size(f))
    def unlock_file(f):
        msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, file_size(f))


# Class for ensuring that all file operations are atomic, treat
# initialization like a standard call to 'open' that happens to be atomic.
# This file opener *must* be used in a "with" block.
class AtomicOpen:
    # Open the file with arguments provided by user. Then acquire
    # a lock on that file object (WARNING: Advisory locking).
    def __init__(self, path, *args, **kwargs):
        # Open the file and acquire a lock on the file before operating
        self.file = open(path,*args, **kwargs)
        # Lock the opened file
        lock_file(self.file)

    # Return the opened file object (knowing a lock has been obtained).
    def __enter__(self, *args, **kwargs): return self.file

    # Unlock the file and close the file object.
    def __exit__(self, exc_type=None, exc_value=None, traceback=None):        
        # Flush to make sure all buffered contents are written to file.
        self.file.flush()
        os.fsync(self.file.fileno())
        # Release the lock on the file.
        unlock_file(self.file)
        self.file.close()
        # Handle exceptions that may have come up during execution, by
        # default any exceptions are raised to the user.
        if (exc_type != None): return False
        else:                  return True        
Run Code Online (Sandbox Code Playgroud)

现在,AtomicOpen可以在with通常使用open语句的块中使用.

警告:如果在Windows上运行并且在调用exit之前Python崩溃,我不确定锁定行为是什么.

警告:此处提供的锁定是建议性的,而不是绝对的.所有潜在的竞争过程都必须使用"AtomicOpen"类.


小智 14

我更喜欢lockfile - 与平台无关的文件锁定

  • 这个库似乎编写得很好,但是没有用于检测过时锁文件的机制.它跟踪创建锁的PID,因此应该可以判断该进程是否仍在运行. (3认同)
  • 请注意,该库已被取代,并且是https://github.com/harlowja/fasteners的一部分 (2认同)

Ric*_*eur 12

锁定是特定于平台和设备的,但通常,您有以下几种选择:

  1. 使用flock()或等效的(如果你的os支持它).这是建议锁定,除非你检查锁,否则忽略它.
  2. 使用lock-copy-move-unlock方法,您可以在其中复制文件,写入新数据,然后移动它(移动,而不是复制 - 移动是Linux中的原子操作 - 检查您的操作系统),然后检查存在锁文件.
  3. 使用目录作为"锁定".如果您正在写NFS,这是必要的,因为NFS不支持flock().
  4. 还有可能在进程之间使用共享内存,但我从未尝试过; 它非常特定于操作系统.

对于所有这些方法,您必须使用自旋锁(重试失败后)技术来获取和测试锁.这确实为错误同步留下了一个小窗口,但它通常小到不成为主要问题.

如果您正在寻找跨平台的解决方案,那么您最好通过其他机制登录到另一个系统(下一个最好的方法是上面的NFS技术).

请注意,sqlite受到与普通文件相同的NFS约束,因此您无法在网络共享上写入sqlite数据库并免费获得同步.

  • 注意:在Win32中,移动/重命名不是原子的.参考:http://stackoverflow.com/questions/167414/is-an-atomic-file-rename-with-overwrite-possible-on-windows (4认同)
  • 新注释:自Python 3.3以来,`os.rename`现在在Win32中是原子的:https://bugs.python.org/issue8828 (4认同)

Max*_*ues 12

我一直在寻找几种解决方案,我的选择是 oslo.concurrency

它功能强大,记录相对较好.它基于紧固件.

其他方案:

  • 还有[`filelock`(最后发布时间:2019 年 5 月 18 日发表评论时)](https://pypi.org/project/filelock/) (5认同)

Jos*_*eia 10

下面是如何使用filelock库的示例,它类似于Evan Fosmark 的实现

from filelock import FileLock

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

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

  • 这并不是对埃文答案的补充,因为它与埃文的答案完全正交,尽管你自己可能没有意识到这一点!有点令人困惑的是,您在答案中链接到的 PyPI 上的 [`filelock`](https://pypi.org/project/filelock/) 模块,就像 Evan 的模块公开了一个 `FileLock` 类,与 Evan 的模块完全无关工作。您可以在 GitHub 上看到 Evan 的代码(https://github.com/dmfrey/FileLock/blob/master/filelock/filelock.py)与 https://github.com/tox 上的代码没有共享代码或祖先。 dev/py-filelock/tree/main/src/filelock,这就是您在这里使用的。 (4认同)

Kev*_*vin 7

协调对操作系统级别的单个文件的访问充满了您可能不想解决的各种问题.

您最好的选择是有一个单独的进程来协调对该文件的读/写访问.

  • "协调对该文件的读/写访问的独立进程" - 换句话说,实现数据库服务器:-) (19认同)
  • -1因为这只是FUD而没有解释.锁定文件进行写入对我来说似乎是一个非常简单的概念,操作系统提供了像[`flock`](http://linux.die.net/man/2/flock)这样的函数."滚动你自己的互斥体和一个守护进程来管理它们"的方法似乎是一种相当极端和复杂的方法来解决......你实际上没有告诉过我们的问题,但只是scally建议存在. (9认同)

Gre*_*ill 5

锁定文件通常是特定于平台的操作,因此您可能需要允许在不同的操作系统上运行.例如:

import os

def my_lock(f):
    if os.name == "posix":
        # Unix or OS X specific locking here
    elif os.name == "nt":
        # Windows specific locking here
    else:
        print "Unknown operating system, lock unavailable"
Run Code Online (Sandbox Code Playgroud)

  • 您可能已经知道这一点,但平台模块也可用于获取运行平台上的信息.platform.system().http://docs.python.org/library/platform.html. (6认同)