2012-03-28 5 views
17

В операционной системе на базе Debian (Ubuntu, Debian Squeeze) я использую fttnt(), чтобы заблокировать файл. Как я понимаю из того, что я читал, fnctl.flock блокирует файл таким образом, что будет вызываться исключение, если другой клиент хочет заблокировать один и тот же файл.Python fcntl не заблокирован, как ожидалось

Я построил маленький пример, который я бы ожидать, чтобы бросить 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") 

Но пример просто печатает «Нет ошибки».

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

Какое объяснение этого поведения?

EDIT:

Изменения по просьбе nightcracker, эта версия также не печатает "Нет ошибки", хотя я бы не стал ожидать, что:

#!/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") 
+0

Существует [catch о блокировке файлов в рамках одного процесса] (http://0pointer.de/blog/projects/locking.html). Таким образом, в рамках одного процесса потоки будут совместно использовать блокировку файлов. – bouke

ответ

7

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

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

Я получаю сообщение «Нет ошибки» :(Знаете ли вы, почему? – Etam

+16

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

+0

Этот ответ вводит в заблуждение. Замечание, приведенное выше, @Dustin верно. –

3

Есть два уловы. Согласно documentation:

  1. Когда операция LOCK_SH или LOCK_EX, он также может быть побитовое ORed с LOCK_NB, чтобы избежать блокировки на блокировку приобретения. Если используется LOCK_NB, и блокировка не может быть получена, то будет поднят IOError, и исключение будет иметь атрибут errno, установленный на EACCES или EAGAIN (в зависимости от операционной системы, для переносимости, проверьте оба значения).

    Вы забыли установить LOCK_NB.

  2. По крайней мере в некоторых системах LOCK_EX может быть использован только если дескриптор файла ссылается на файл открыт для записи.

    У вас есть файл, открытый для чтения, который может не поддерживать LOCK_EX в вашей системе.

+0

спасибо. Но оба изменения (добавление fcntl.LOCK_NB к первой блокировке и открытие файла для записи) не изменяют поведение, все еще получая «Нет ошибки». – ifischer

+1

@ifischer: работает ли 'fcntl' с C на вашей системе? Какую версию Python вы используете? – orlp

+0

Пробовал оба Python 2.7.1 и Python 3.2. Попробуем C позже – ifischer

0

Необходимо передать в дескриптор файла (доступный путем вызова метода fileno() объекта файла). Код ниже бросает IOError, когда один и тот же код запускается в отдельном интерпретаторе.

>>> import fcntl 
>>> thefile = open('/tmp/testfile') 
>>> fd = thefile.fileno() 
>>> fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) 
+0

Это не обязательно. Из [документации] (http://docs.python.org/library/fcntl.html#fcntl.flock): _Переформируйте операцию блокировки op на файловом дескрипторе fd (также принимаются объекты __file, предоставляющие метод fileno()__) ._ – orlp

+0

Применение этого не изменяет поведения, все еще получая «Нет ошибки», в отличие от того, что я ожидал бы – ifischer

+0

@ifischer: Нечетный, я вставил код выше в два интерпретатора python на машине Ubuntu и первый из них был завершен, второй бросил исключение. – Vatine

10

Старый пост, но если кто-то находит его, я получаю такое поведение:

>>> 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 

Похоже, что в первом случае, файл закрывается после первой строки, предположительно потому, что объектный файл недоступен , Закрытие файла освобождает блокировку.

10

я мастерил та же проблема ... Я решил, что держит открытый файл в отдельной переменной:

не будет работать:

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) 

Я думаю, что первый не работает, потому что открыт файл сбор мусора, закрыт и блокировка выпущена.

+3

Обратите внимание, что вы используете 'lockf', тогда как OP использовал' flock' в исходном сообщении. Эти два очень разные реализации! Плохие имена, трудно поймать;) –

0

Try:

global f 
f = open('/tmp/locktest', 'r') 

Когда файл закрывается замок исчезнет.

+0

Если это два разных процесса, то это абсолютно бесполезно. Должен использовать fcntl.flock – theBuzzyCoder