Python: блокировка каталога

1

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.

  • 0
    Является ли ваш собственный процесс Python единственным кандидатом для работы с каталогом?
  • 0
    @FlyingTeller есть другой процесс, который помещает файлы в этот каталог, но его не нужно блокировать. Только скрипт python, который потребляет данные, нуждается в блокировке.
Показать ещё 6 комментариев
Теги:
locking

4 ответа

1
Лучший ответ

Я немного меняю свой код, добавляю 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()
  • 0
    Отлично. Есть ли один интересный способ сделать небольшую библиотеку Python на github + pypi из этого?
2

Если все, что вам нужно, это блокировка чтения, то в коде есть только незначительная ошибка. Вполне возможно получить блокировку чтения в каталоге.

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

0

Я нашел ответ здесь: Python: Lock directory

Можно заблокировать каталог следующим образом:

fcntl.flock(self.dir_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)

Конечно, это блокировка, которую каждый код, который играет в этой игре, должен сначала проверять.

AFAIK это называется "консультативный замок".

  • 0
    Вам не нужно больше это хранить. Я объединил вопросы. Пожалуйста, не задавайте повторяющиеся вопросы, как это снова.
  • 0
    @YvetteColomb, что произойдет, если я сделаю это снова?
Показать ещё 1 комментарий
-1

Я предлагаю вам пойти с простым файлом блокировки. Как указывает вопрос в комментарии (Как заблокировать каталог между процессами python в Linux?), Механизм каталогов для каталогов не существует, а не файлы.
Файлы блокировки используются слева и справа в Linux, они очень прозрачны и легко отлаживаются, поэтому я бы просто пошел с этим.
Я ожидаю, что вас оспаривают на этом, однако!

  • 1
    Каталоги поддерживают консультативную блокировку просто отлично, этот другой ответ просто неверен. Все, что вам нужно, - это дескриптор файла, а низкоуровневый os.open() позволит вам просто отлично открыть дескриптор файла каталога.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню