我需要在Python中为写入锁定一个文件。它将被多个Python进程同时访问。我在网上找到了一些解决方案,但大多数都无法满足我的需求,因为它们通常只适用于Unix或Windows操作系统。
我需要在Python中为写入锁定一个文件。它将被多个Python进程同时访问。我在网上找到了一些解决方案,但大多数都无法满足我的需求,因为它们通常只适用于Unix或Windows操作系统。
好的,所以我最终选择了我写的代码这里,在我的网站上链接已失效,请在archive.org上查看(也可在GitHub上获取)。我可以按以下方式使用它:
from filelock import FileLock
with FileLock("myfile.txt.lock"):
# work with the file as it is now locked
print("Lock acquired.")
其他解决方案引用了许多外部代码库。如果你想自己完成,这里有一些跨平台解决方案的代码,它使用Linux / DOS系统上各自的文件锁定工具。
try:
# Posix based file locking (Linux, Ubuntu, MacOS, etc.)
# Only allows locking on writable files, might cause
# strange results for reading.
import fcntl, os
def lock_file(f):
if f.writable(): fcntl.lockf(f, fcntl.LOCK_EX)
def unlock_file(f):
if f.writable(): 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
语句一样。
警告:
fcntl.lock
锁定只读文件已成为非法操作。__exit__
中,在 unlock_file
之后,你需要在锁外部进行 close
。我相信运行时可能会在 close
过程中刷新(即写入)数据。我认为必须在锁下执行 flush
和 fsync
,以确保在 close
过程中不会写入额外的数据到锁外部。 - Benjamin Bannierself.file = open('somefile.txt', 'r')
和 lock_file(self.file)
之间调用 self.file = open('somefile.txt', 'w')
,事情仍然可能出错,对吗? - jezmsvcrt.LK_RLCK
是读锁,因此与 fcntl.LOCK_SH
相同,而 msvcrt.LK_LOCK
是排他锁,类似于 fcntl.LOCK_EX
。因此,在 Windows 下,您可能必须使用 msvcrt.LK_LOCK
。然而,像往常一样,文档很糟糕,我没有 Windows 来验证,抱歉。 - Tino这里有一个跨平台的文件锁定模块: Portalocker
虽然正如 Kevin 所说,尽可能避免多个进程同时写入同一文件。
如果你可以将问题塞进数据库中,那么就可以使用 SQLite。它支持并发访问,并处理自己的锁定。
我一直在寻找几种解决方案,并选择了oslo.concurrency
它很强大且文档相对详细,基于fasteners。
其他解决方案:
filelock
包不是与被接受的答案相同吗? 安装方法为:pip3 install filelock
。 - alperfilelock
比被采纳答案的包有更多的 Star 和 forks。 - alper我更喜欢 lockfile — 跨平台文件锁定
锁定是平台和设备特定的,但通常有几个选项:
对于所有这些方法,您都必须使用旋转锁定(失败后重试)技术来获取和测试锁定。这确实留下了小的错误同步窗口,但通常足够小,不会成为主要问题。
如果您正在寻找跨平台的解决方案,则最好通过其他机制记录到另一个系统(上面提到的NFS技术是次佳选择)。
请注意,sqlite在NFS上也受到与普通文件相同的限制,因此无法在网络共享上写入sqlite数据库并免费同步。
os.rename
函数进行重命名操作时已经具有原子性。详见:https://bugs.python.org/issue8828 - Ghostkeeper以下是使用filelock库的示例,该库类似于Evan Fossmark的实现:
from filelock import FileLock
lockfile = r"c:\scr.txt"
lock = FileLock(lockfile + ".lock")
with lock:
file = open(path, "w")
file.write("123")
file.close()
with lock:
块内的任何代码都是线程安全的,这意味着在另一个进程访问该文件之前,它将完成。
filelock
模块(https://pypi.org/project/filelock/),该模块与Evan的模块都公开了一个`FileLock`类,但它们是完全不相关的。您可以在GitHub查看,Evan的代码位于https://github.com/dmfrey/FileLock/blob/master/filelock/filelock.py,并未与您在此处使用的https://github.com/tox-dev/py-filelock/tree/main/src/filelock的代码共享任何代码或祖先关系。 - Mark Amery在操作系统级别协调对单个文件的访问存在各种问题,您可能不想解决。
最好的方法是有一个单独的进程来协调对该文件的读/写访问。
flock
这样的函数一样,将文件锁定以进行写入似乎是一个非常简单明了的概念,操作系统可以提供这种功能。你们采用“自己编写互斥锁和管理它们的守护进程”的方法,似乎是一种极端而复杂的方法来解决一个问题,但你们并没有告诉我们这个问题具体是什么,只是可怕地暗示它的存在。 - Mark Amery锁定文件通常是平台特定的操作,因此您可能需要考虑在不同操作系统上运行的可能性。例如:
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"
import os
import json
def Server():
i = 0
while i == 0:
try:
with open(File_Temp, "w") as file:
json.dump(DATA, file, indent=2)
if os.path.exists(File_A):
os.remove(File_A)
os.rename(File_Temp, File_A)
i = 1
except OSError as e:
print ("file locked: " ,str(e))
time.sleep(1)
def Clients():
i = 0
while i == 0:
try:
if os.path.exists(File_A):
with open(File_A,"r") as file:
DATA_Temp = file.read()
DATA = json.loads(DATA_Temp)
i = 1
except OSError as e:
print (str(e))
time.sleep(1)