Python fcntl не блокируется, как ожидалось
В ОС на основе Debian (Ubuntu, Debian Squeeze) я использую fttnt(), чтобы заблокировать файл. Как я понимаю из того, что я читал, fnctl.flock блокирует файл таким образом, что будет вызываться исключение, если другой клиент хочет заблокировать один и тот же файл.
Я построил небольшой пример, который я бы ожидал, чтобы выбросить excepiton, поскольку я сначала заблокировал файл, а затем, сразу после этого, я снова попытаюсь заблокировать его:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import fcntl
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX)
try:
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
print("can't immediately write-lock the file ($!), blocking ...")
else:
print("No error")
Но пример просто печатает "Без ошибок".
Если я разорву этот код до двух клиентов, работающих одновременно (одна блокировка, а затем ожидание, другая попытка блокировки после первой блокировки уже активна), я получаю такое же поведение - никакого эффекта вообще.
Какое объяснение этого поведения?
ИЗМЕНИТЬ
Изменяется по требованию ночного скребка, эта версия также выводит "Нет ошибки", хотя я не ожидал бы этого:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import fcntl
import time
fcntl.flock(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
try:
fcntl.flock(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
print("can't immediately write-lock the file ($!), blocking ...")
else:
print("No error")
Ответы
Ответ 1
Получил это. Ошибка в моем script заключается в том, что я создаю новый дескриптор файла для каждого вызова:
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
(...)
fcntl.flock(open('/tmp/locktest', 'r'), fcntl.LOCK_EX | fcntl.LOCK_NB)
Вместо этого я должен назначить объект файла переменной и попытаться заблокировать:
f = open('/tmp/locktest', 'r')
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
(...)
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
Чем я получаю исключение, которое я хотел увидеть: IOError: [Errno 11] Resource temporarily unavailable
. Теперь я должен думать о том, в каких случаях имеет смысл использовать fcntl.
Ответ 2
Старый пост, но если кто-нибудь еще найдет его, я получаю следующее:
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX)
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
# That didn't throw an exception
>>> f = open('test.flock', 'w')
>>> fcntl.flock(f, fcntl.LOCK_EX)
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: [Errno 35] Resource temporarily unavailable
>>> f.close()
>>> fcntl.flock(open('test.flock', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
# No exception
Похоже, что в первом случае файл закрывается после первой строки, по-видимому, потому, что файл-объект недоступен. Закрытие файла освобождает блокировку.
Ответ 3
У меня была та же проблема... Я решил, что он держит открытый файл в отдельной переменной:
Не работает:
fcntl.lockf(open('/tmp/locktest', 'w'), fcntl.LOCK_EX | fcntl.LOCK_NB)
Работает:
lockfile = open('/tmp/locktest', 'w')
fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
Я думаю, что первый не работает, потому что открытый файл собранный мусор, закрыт и заблокирован.
Ответ 4
Есть два улова. В соответствии с документацией:
-
Когда операция LOCK_SH
или LOCK_EX
, она также может быть побитовой ORed с помощью LOCK_NB
, чтобы избежать блокировки при сборе блокировки. Если используется LOCK_NB
, и блокировка не может быть получена, будет добавлен IOError
, и исключение будет иметь атрибут errno
, установленный на EACCES
или EAGAIN
(в зависимости от операционной системы, для переносимости, проверьте для обоих значений).
Вы забыли установить LOCK_NB
.
-
По крайней мере в некоторых системах LOCK_EX
может использоваться, только если дескриптор файла ссылается на файл, открытый для записи.
У вас есть файл, открытый для чтения, который может не поддерживать LOCK_EX
в вашей системе.
Ответ 5
Вам нужно передать файловый дескриптор (доступный путем вызова метода fileno() объекта файла). Ниже приведен код IOError, когда один и тот же код запускается в отдельном интерпретаторе.
>>> import fcntl
>>> thefile = open('/tmp/testfile')
>>> fd = thefile.fileno()
>>> fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
Ответ 6
Try:
global f
f = open('/tmp/locktest', 'r')
Когда файл закрыт, замок будет исчезать.