从锁定的文件加载json

时间:2018-05-23 09:45:55

标签: python json file locking

我有几个相同的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会抱怨文件格式无效。

如何正确设置?

2 个答案:

答案 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脚本的多个实例。

来源:www.tutorialspoint.comPython Docs