我需要锁定一个文件以便用Python编写.它将同时从多个Python进程访问.我在网上找到了一些解决方案,但大多数都失败了,因为它们通常只基于Unix或基于Windows.
好吧,所以我最终得到了我在这里写的代码,在我的网站链接已经死了,在archive.org上查看(也可以在GitHub上找到).我可以用以下方式使用它:
from filelock import FileLock with FileLock("myfile.txt"): # work with the file as it is now locked print("Lock acquired.")
这里有一个跨平台文件锁定模块:Portalocker
虽然凯文说,但是如果可能的话,你想要立即从多个进程写入文件.
如果您可以将问题塞进数据库,则可以使用SQLite.它支持并发访问并处理自己的锁定.
其他解决方案引用了许多外部代码库.如果您更愿意自己动手,这里有一些跨平台解决方案的代码,它使用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
现在,AtomicOpen
可以在with
通常使用open
语句的块中使用.
警告:如果在Windows上运行并且在调用exit之前Python崩溃,我不确定锁定行为是什么.
警告:此处提供的锁定是建议性的,而不是绝对的.所有潜在的竞争过程都必须使用"AtomicOpen"类.
我更喜欢lockfile - 与平台无关的文件锁定
锁定是特定于平台和设备的,但通常,您有以下几种选择:
使用flock()或等效的(如果你的os支持它).这是建议锁定,除非你检查锁,否则忽略它.
使用lock-copy-move-unlock方法,您可以在其中复制文件,写入新数据,然后移动它(移动,而不是复制 - 移动是Linux中的原子操作 - 检查您的操作系统),然后检查存在锁文件.
使用目录作为"锁定".如果您正在写NFS,这是必要的,因为NFS不支持flock().
还有可能在进程之间使用共享内存,但我从未尝试过; 它非常特定于操作系统.
对于所有这些方法,您必须使用自旋锁(重试失败后)技术来获取和测试锁.这确实为错误同步留下了一个小窗口,但它通常小到不成为主要问题.
如果您正在寻找跨平台的解决方案,那么您最好通过其他机制登录到另一个系统(下一个最好的方法是上面的NFS技术).
请注意,sqlite受到与普通文件相同的NFS约束,因此您无法在网络共享上写入sqlite数据库并免费获得同步.
我一直在寻找几种解决方案,我的选择是 oslo.concurrency
它功能强大,记录相对较好.它基于紧固件.
其他方案:
Portalocker:需要pywin32,这是一个exe安装,所以不能通过pip
紧固件:记录不完整
lockfile:不推荐使用
flufl.lock:POSIX系统的NFS安全文件锁定.
simpleflock:上次更新2013-07
zc.lockfile:上次更新2016-06(截至2017-03)
lock_file:2007-10上次更新
协调对操作系统级别的单个文件的访问充满了您可能不想解决的各种问题.
您最好的选择是有一个单独的进程来协调对该文件的读/写访问.
锁定文件通常是特定于平台的操作,因此您可能需要允许在不同的操作系统上运行.例如:
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"