AFAIK этот код можно использовать для блокировки каталога:
class LockDirectory(object):
def __init__(self, directory):
assert os.path.exists(directory)
self.directory = directory
def __enter__(self):
self.dir_fd = os.open(self.directory, os.O_RDONLY)
try:
fcntl.flock(self.dir_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError as ex:
if ex.errno != errno.EAGAIN:
raise
raise Exception('Somebody else is locking %r - quitting.' % self.directory)
def __exit__(self, exc_type, exc_val, exc_tb):
self.dir_fd.close()
Но в соответствии с ответами на этот вопрос блокировка каталога невозможна: Python: заблокировать каталог
Что не так с вышеуказанным кодом?
Мне нужно только поддерживать текущую версию Linux. Нет Windows, Mac или другого unix.
Я немного меняю свой код, добавляю return self
как это делает большинство контекстных обработок, а затем с dup()
, во втором управлении контекстом произойдет сбой. И решение прост, fcntl.flock(self.dir_fd,fcntl.LOCK_UN)
Режим, используемый для открытия файла, не имеет значения для стекания.
и вы не можете стекаться на NFS.
import os
import fcntl
import time
class LockDirectory(object):
def __init__(self, directory):
assert os.path.exists(directory)
self.directory = directory
def __enter__(self):
self.dir_fd = os.open(self.directory, os.O_RDONLY)
try:
fcntl.flock(self.dir_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError as ex:
raise Exception('Somebody else is locking %r - quitting.' % self.directory)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# fcntl.flock(self.dir_fd,fcntl.LOCK_UN)
os.close(self.dir_fd)
def main():
with LockDirectory("test") as lock:
newfd = os.dup(lock.dir_fd)
with LockDirectory("test") as lock2:
pass
if __name__ == '__main__':
main()
Если все, что вам нужно, это блокировка чтения, то в коде есть только незначительная ошибка. Вполне возможно получить блокировку чтения в каталоге.
Вам нужно будет изменить функцию __exit__
чтобы использовать os.close()
чтобы закрыть дескриптор файла; файловый дескриптор - это просто целое число, а целые числа не .close()
метода .close()
:
def __exit__(self, exc_type, exc_val, exc_tb):
os.close(self.dir_fd)
Обычная путаница для людей, которые думают, что вы не можете, это те, которые пытались использовать функцию open()
. Python не позволит вам открыть узел каталогов с этой функцией, потому что нет смысла создавать объект файла Python для каталога. Или, возможно, существует предположение о том, что вы хотели, чтобы ОС обеспечивала доступ к каталогу через блокировку (в отличие от консультативной блокировки, с которой совлокальный набор процессов соглашается получить сначала до попытки доступа).
Так что нет, нет ничего плохого в коде, если все, что вам нужно, это консультативная блокировка, и все в порядке с этим, только работая над Linux.
Я бы отбросил разницу в directory
от кода. Блокировка будет работать по любому пути, к которому вы имеете доступ для чтения. Это не только справочники.
Недостатком блокировки каталога является то, что это не дает вам места для хранения метаданных блокировки. В то время как lsof
может предоставить вам PID текущего владельца блокировки, вы можете lsof
некоторую другую информацию с блокировкой, чтобы помочь устранить неисправность или автоматизировать блокировку. Файл .lock
или символическая ссылка позволят вам записать дополнительную информацию. Например, Mercurial создаст символическую ссылку с именем хоста, идентификатором пространства имен PID (только для Linux) и PID в целевом имени; вы можете создать такую символическую ссылку атомарно, в то время как запись этих данных в файл потребует создания файла под временным именем, за которым следует переименование.
Я нашел ответ здесь: Python: Lock directory
Можно заблокировать каталог следующим образом:
fcntl.flock(self.dir_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
Конечно, это блокировка, которую каждый код, который играет в этой игре, должен сначала проверять.
AFAIK это называется "консультативный замок".
Я предлагаю вам пойти с простым файлом блокировки. Как указывает вопрос в комментарии (Как заблокировать каталог между процессами python в Linux?), Механизм каталогов для каталогов не существует, а не файлы.
Файлы блокировки используются слева и справа в Linux, они очень прозрачны и легко отлаживаются, поэтому я бы просто пошел с этим.
Я ожидаю, что вас оспаривают на этом, однако!
os.open()
позволит вам просто отлично открыть дескриптор файла каталога.