我有几个相同的python脚本并行运行的实例,读取和写入相同的json文件:首先,一个实例从json文件中读取信息,然后处理它然后锁定它然后再次读取它,以获得提升到目前为止文件的内容(可能已被其他实例更改)然后写入并释放锁。嗯,就是说,如果它......工作,它就会起作用
我脚本中的锁定和写入部分的精简版本如下所示:
import json
import fcntl
data = json.load(open('test.json'))
# do things with data
with open('test.json', 'w+') as file:
fcntl.flock(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
data = json.load(open('test.json'))
fcntl.flock(file, fcntl.LOCK_UN)
但open函数似乎有点清除文件,因为在运行此代码片段后它将为空,而json会抱怨文件格式无效。
如何正确设置?
答案 0 :(得分:0)
但是open函数似乎有点清楚文件
是的,在w
写入模式下打开文件总是清除文件;来自open()
function documentation:
'w'
打开写入,先截断文件[...] 默认模式为
'r'
(打开以阅读文本,'rt'
的同义词)。对于二进制读写访问,模式'w+b'
打开并将文件截断为0字节。'r+b'
打开文件而不截断。
您希望在>>截断文件之前锁定文件。您也可以在'r+'
模式下打开文件(读取和写入),此时您需要在锁定后手动截断它。
您还需要锁定文件以供阅读,因为您不希望读者在尝试阅读时遇到截断的数据,而另一个进程正在忙于替换内容。使用共享锁,此时允许其他进程获取共享锁 ,这使得许多进程可以读取数据而无需彼此等待。想要编写的进程必须获取一个独占锁,只有在没有共享锁时才能获得该锁。
就个人而言,我创建了一个处理锁定的上下文管理器(以独占模式进行写入,或者以共享模式进行读取),并且只在获取锁定后截断文件。您还需要考虑尚未存在的文件,如果您不想永远等待锁定,则需要处理超时(意味着您需要使用LOCK_NB
在一个循环中并测试返回值以查看是否获得了锁,直到经过了一定的时间。)
在下面的上下文管理器中,我使用os.open()
低级系统调用来确保在尝试锁定它以进行独占访问时创建该文件,而不会截断它(如果它已经存在) :
import errno
import fcntl
import os
import time
class Timeout(Exception):
"""Could not obtain a lock within the time given"""
class LockedFile:
"""Lock and open a file.
If the file is opened for writing, an exclusive lock is used,
otherwise it is a shared lock
"""
def __init__(self, path, mode, timeout=None, **fileopts):
self.path = path
self.mode = mode
self.fileopts = fileopts
self.timeout = timeout
# lock in exclusive mode when writing or appending (including r+)
self._exclusive = set('wa+').intersection(mode)
self._lockfh = None
self._file = None
def _acquire(self):
if self._exclusive:
# open the file in write & create mode, but *without the
# truncate flag* to make sure it is created only if it
# doesn't exist yet
lockfhmode, lockmode = os.O_WRONLY | os.O_CREAT, fcntl.LOCK_EX
else:
lockfhmode, lockmode = os.O_RDONLY, fcntl.LOCK_SH
self._lockfh = os.open(self.path, lockfhmode)
start = time.time()
while True:
try:
fcntl.lockf(self._lockfh, lockmode | fcntl.LOCK_NB)
return
except OSError as e:
if e.errno not in {errno.EACCES, errno.EAGAIN}:
raise
if self.timeout is not None and time.time() - start > self.timeout:
raise Timeout()
time.sleep(0.1)
def _release(self):
fcntl.lockf(self._lockfh, fcntl.LOCK_UN)
os.close(self._lockfh)
def __enter__(self):
if self._file is not None:
raise LockException('Lock already taken')
self._acquire()
self._file = open(self.path, self.mode, **self.fileopts)
return self._file
def __exit__(self, *exc):
if self._file is None:
raise LockException('Not locked')
self._file.close()
self._file = None
self._release()
尝试读取文件的进程然后使用:
with LockedFile('test.json', 'r') as file:
data = json.load(file)
以及想要编写的进程使用:
with LockedFile('test.json', 'w') as file:
json.dump(data, file)
如果要允许超时,请在try/except
块周围添加with
块并捕获Timeout
异常;你需要决定当时会发生什么:
try:
with LockedFile('test.json', 'w', timeout=10) as file:
json.dump(data, file)
except Timeout:
# could not acquire an exclusive lock to write the file. What now?
答案 1 :(得分:-1)
您使用“w +”打开文件。
W + 打开文件进行书写和阅读。如果文件存在,则覆盖现有文件。如果该文件不存在,则创建一个用于读写的新文件。
而不是w+
使用a
。
在我看来,您可以使用线程库或多处理来使用Locks以更优雅的方式执行此操作,而不是运行相同python脚本的多个实例。