Вы в основном разработали версию двоичного семафора (или мьютекса) для файловой системы. Это хорошо изученная структура, используемая для блокировки, поэтому, если вы правильно поняли детали реализации, она должна работать. Хитрость заключается в том, чтобы операция «проверить и установить» или, в вашем случае, «проверить существование и движение», была действительно атомарной. Для этого я бы использовал что-то вроде этого:
lock_acquired = False
while not lock_acquired:
try:
move(fh, fhtemp)
except:
sleep(3)
else:
lock_acquired = True
# do your writing
move(fhtemp, fh)
lock_acquired = False
Программа, которая у вас была, работала бы большую часть времени, но, как уже упоминалось, у вас могут возникнуть проблемы, если другой процесс переместит файл между проверкой его существования и вызовом move
. Я полагаю, вы могли бы обойти это, но я лично рекомендую придерживаться хорошо проверенного алгоритма мьютекса. (Я перевел / портировал приведенный выше пример кода из Современные операционные системы Эндрю Таненбаума, но возможно, что я внес ошибки при преобразовании - просто честное предупреждение)
Кстати, справочная страница для функции open
в Linux предлагает следующее решение для блокировки файлов:
Решение для выполнения атомарной блокировки файлов с использованием файла блокировки состоит в том, чтобы создать уникальный файл в той же файловой системе (например, включающий имя хоста и pid), используя ссылку (2), чтобы создать ссылку на файл блокировки. Если link () возвращает 0, блокировка успешна. В противном случае используйте stat (2) для уникального файла, чтобы проверить, увеличилось ли количество ссылок до 2, и в этом случае блокировка также будет успешной.
Чтобы реализовать это в Python, вы можете сделать что-то вроде этого:
# each instance of the process should have a different filename here
process_lockfile = '/path/to/hostname.pid.lock'
# all processes should have the same filename here
global_lockfile = '/path/to/lockfile'
# create the file if necessary (only once, at the beginning of each process)
with open(process_lockfile, 'w') as f:
f.write('\n') # or maybe write the hostname and pid
# now, each time you have to lock the file:
lock_acquired = False
while not lock_acquired:
try:
link(process_lockfile, global_lockfile)
except:
lock_acquired = (stat(process_lockfile).st_nlinks == 2)
else:
lock_acquired = True
# do your writing
unlink(global_lockfile)
lock_acquired = False