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 - независимая от платформы блокировка файлов
блокировка зависит от платформы и устройства, но, как правило, у вас есть несколько вариантов:
- используйте flock () или эквивалент (если ваша ОС поддерживает его). Это консультативная блокировка, Если вы не проверяете блокировку, ее игнорируют.
- используйте методику блокировки-копирования-перемещения-разблокировки, где вы копируете файл, записываете новые данные, а затем перемещаете его (перемещение, а не копирование - перемещение-это атомарная операция в Linux-проверьте свою ОС), и вы проверяете наличие файла блокировки.
- использовать каталог как "замок". Это необходимо, если вы пишете в NFS, так как NFS не поддерживает flock().
- существует также возможность использования общей памяти между процессами, но я никогда не пробовал это; это очень специфично для ОС.
для всех этих методов вам придется использовать метод 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" может быть использован везде, где можно было бы нормально использовать "открытое" заявление.
предупреждение: если работает на Windows и Python аварийно завершает работу до выход называется, Я не уверен, что поведение блокировки бы быть.
предупреждение: блокировка, представленная здесь, является консультативной, а не абсолютной. Все потенциально конкурирующие процессы должны использовать класс "AtomicOpen".
координация доступа к одному файлу на уровне ОС чревата всевозможными проблемами, которые вы, вероятно, не хотите решать.
лучше всего иметь отдельный процесс, который координирует доступ на чтение / запись к этому файлу.
Я посмотрел несколько решений, чтобы сделать это и мой выбор Осло.параллелизм
Он мощный и относительно хорошо документирован. Он основан на креплениях.
других решений:
- Portalocker: требуется pywin32, который является установкой exe, поэтому невозможно через pip
- крепеж: плохо документированы
- lockfile: устарел
- flufl.замок: 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"
Я нашел простой и работал(!)реализация от седого-питон.
простое использование ОС.открытый.(.., 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
Comments