Блокировка файла в Python

Мне нужно заблокировать файл для записи на Python. Он будет доступен сразу из нескольких процессов Python. Я нашел некоторые решения в интернете, но большинство из них не подходят для моих целей, поскольку они часто основаны только на Unix или Windows.

12 ответов


хорошо, поэтому я закончил с кодом, который я написал здесь, на мой сайтссылка мертвая, вид на 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. Он поддерживает параллельный доступ и обрабатывает собственную блокировку.


предпочитаю lockfile-независимая от платформы блокировка файлов


блокировка специфична для платформы и устройства, но, как правило, у вас есть несколько вариантов:

  1. используйте flock () или эквивалент (если ваша ОС поддерживает его). Это консультативная блокировка, Если вы не проверяете блокировку, ее игнорируют.
  2. используйте методику блокировки-копирования-перемещения-разблокировки, где вы копируете файл, записываете новые данные, а затем перемещаете его (перемещение, а не копирование - перемещение-атомарная операция в Linux-проверьте свою ОС), и вы проверяете наличие файла блокировки.
  3. использовать каталог как "замок". Это необходимо, если вы пишете в NFS, так как NFS не поддерживает flock().
  4. существует также возможность использования общей памяти между процессами, но я никогда не пробовал это; это очень специфично для ОС.

для всех этих методов вам придется использовать метод spin-lock (retry-after-failure) для получения и тестирования блокировки. Это оставляет небольшое окно для неправильной синхронизации, но его обычно достаточно мало, чтобы не быть основной вопрос.

Если вы ищете решение, которое является кросс-платформенным, то вам лучше войти в другую систему через какой-то другой механизм (следующая лучшая вещь-техника NFS выше).

обратите внимание, что sqlite подвержен тем же ограничениям по NFS, что и обычные файлы, поэтому вы не можете писать в базу данных sqlite на сетевом ресурсе и получать синхронизацию бесплатно.


другие решения ссылаются на множество внешних баз кода. Если вы предпочитаете сделать это самостоятельно, вот код для кросс-платформенного решения, которое использует соответствующие инструменты блокировки файлов в системах Linux / DOS.

try:
    # Posix based file locking (Linux, Ubuntu, MacOS, etc.)
    import fcntl
    def lock_file(f):
        fcntl.lockf(f, fcntl.LOCK_EX)
    def unlock_file(f): pass
except ModuleNotFoundError:
    # Windows file locking
    import msvcrt
    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
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

    # Allows users to use the 'close' function if they want, in case
    # the user did not have the AtomicOpen in a "with" block.
    def close(self): self.__exit__()

    # Unlock the file and close the file object
    def __exit__(self, exc_type=None, exc_value=None, traceback=None):        
        # 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 "можно использовать везде, где обычно используется оператор" open".

предупреждение: если работает на Windows и Python аварийно завершает работу до выход называется, Я не уверен, что поведение блокировки бы быть.

предупреждение: блокировка, представленная здесь, является консультативной, а не абсолютной. Все потенциально конкурирующие процессы должны использовать класс "AtomicOpen".


координация доступа к одному файлу на уровне ОС чревата всевозможными проблемами, которые вы, вероятно, не хотите решать.

лучше всего иметь отдельный процесс, который координирует доступ для чтения/записи к этому файлу.


Я рассматривал несколько решений для этого, и мой выбор был Осло.параллелизм

Это мощно и относительно хорошо документировано. Он основан на креплениях.

других решений:

  • Portalocker: требуется pywin32, который является установкой exe, поэтому невозможно через pip
  • крепеж: плохо документированы
  • lockfile: устарел
  • flufl.lock: NFS-безопасная блокировка файлов для систем POSIX.
  • simpleflock : последнее обновление 2013-07
  • zc.файл: последнее обновление 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"

Я нашел простой и работал(!) реализация из grizzled-python.

простая ОС.открытый.(.., O_EXCL) + os.close () не работал на windows.


на сценарий - это как это: Пользователь запрашивает файл, чтобы сделать что-то. Затем, если пользователь отправляет тот же запрос снова, он сообщает пользователю, что второй запрос не выполняется до завершения первого запроса. Вот почему я использую механизм блокировки для решения этой проблемы.

вот мой рабочий код:

from lockfile import LockFile
lock = LockFile(lock_file_path)
status = ""
if not lock.is_locked():
    lock.acquire()
    status = lock.path + ' is locked.'
    print status
else:
    status = lock.path + " is already locked."
    print status

return status

Я работал над такой ситуацией, когда я запускаю несколько копий одной и той же программы из одного каталога/папки и ошибок регистрации. Мой подход состоял в том, чтобы написать "файл блокировки" на диск перед открытием файла журнала. Программа проверяет наличие " файла блокировки "перед продолжением и ждет своей очереди, если" файл блокировки " существует.

вот код:

def errlogger(error):

    while True:
        if not exists('errloglock'):
            lock = open('errloglock', 'w')
            if exists('errorlog'): log = open('errorlog', 'a')
            else: log = open('errorlog', 'w')
            log.write(str(datetime.utcnow())[0:-7] + ' ' + error + '\n')
            log.close()
            remove('errloglock')
            return
        else:
            check = stat('errloglock')
            if time() - check.st_ctime > 0.01: remove('errloglock')
            print('waiting my turn')

изменить--- После обдумывания некоторых комментариев о несвежих замках выше я отредактировал код, чтобы добавить проверку на несвежесть "файл блокировки."Синхронизация нескольких тысяч итераций этой функции в моей системе дала и в среднем 0.002066... секундами раньше:

lock = open('errloglock', 'w')

только после:

remove('errloglock')

поэтому я решил, что начну с 5 раз больше, чтобы указать на застой и отслеживать ситуацию для проблем.

кроме того, когда я работал со временем, я понял, что у меня есть немного кода, который на самом деле не был необходимо:

lock.close()

который у меня был сразу после открытого оператора, поэтому я удалил его в этом редактировании.


вы можете найти pylocker очень полезно. Он может использоваться для блокировки файла или для блокировки механизмов в целом и может быть доступен сразу из нескольких процессов Python.

Если вы просто хотите, чтобы заблокировать файл, вот как это работает:

import uuid
from pylocker import Locker

#  create a unique lock pass. This can be any string.
lpass = str(uuid.uuid1())

# create locker instance.
FL = Locker(filePath='myfile.txt', lockPass=lpass, mode='w')

# aquire the lock
with FL as r:
    # get the result
    acquired, code, fd  = r

    # check if aquired.
    if fd is not None:
        print fd
        fd.write("I have succesfuly aquired the lock !")

# no need to release anything or to close the file descriptor, 
# with statement takes care of that. let's print fd and verify that.
print fd