Блокировка одновременной записи

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

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

Поэтому в CGI-скриптах принимают специальные меры по блокировке одновременного
использования файлов данных несколькими скриптами одновременно. В языке Perl,
как и во многих языках программирования, есть специальная функция "блокировки"
файла flock.

Однако на практике эта функция не работает в портах Perl под Windows 9x. Соответственно,
скрипты, использующие эту функцию, на мой взгляд, не могут считаться кросс-платформенными.
Это также затрудняет отладку скриптов на машине под Windows перед перносом их
на UNIX-хостинг.

Тем не менее, защиту файлов от одновременного использования можно организовать
и "своим путем", и это будет работать на всех системах.

Предлагаемый здесь метод основан на использовании временного файла с заранее
известным именем.

Суть метода простая - перед тем, как "занять" файл данных, скрипт
создает этот временный файл, а после освобождения - удаляет. Таким образом,
наличие этого временного файла означает, что файл данных занят. Другой экземпляр
скрипта перед записью проверяет наличие временного файла и, если он есть, дожидается
его удаления первым экземпляром, и только затем начинает работу с файлом данных.

Имя для временного файла может быть любым - главное, чтобы для блокировки разных
файлов данных разными скриптами использовались разные имена.

Фрагмент Perl-кода, реализующий такую защиту, может быть таким:

$lockfile="data.tmp"; #Имя временного файла блокировки

$count=50; $interval=0.05; #Кол-во попыток и интервал между ними


if (-e $lockfile)

{

#Если временный файл есть, ждем его удаления другим процессом


while (($count>0)&&(-e $lockfile))

{

sleep $interval;

$count-=1;

};

};

if ($count==0){

#Здесь размещается код обработки непредвиденной ошибки

};

open TF,">$lockfile"; #Создаем временный файл

close TF;

#...

#Здесь размещается собственно код работы с файлом данных

#...

unlink $lockfile; #Удаляем временный файл

Этот участок скрипта проверяет, существует ли временный файл. Если он существует,
то производится ($count) проверок его существования через интервалы ($interval)
секунд (значения 50 и 0.05 можно заменить своими; предполагается, что время
($count*$interval) более чем достаточное, чтобы другой процесс завершил работу
с файлом данных). Как только временный файл будет удален другим процессом, произойдет
выход из цикла; далее скрипт создает свой временный файл, осущетсвляет работу
с файлом данных и удаляет временный файл.

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

Во многих случаях имеет смысл "блокировать" файл данных не только
во время записи в него, а во время всего цикла "чтение-модификация-запись".
В противном случае очень возможна потеря результатов работы одного из "экземпляров"
скрипта.

На мой взгляд, этот подход не менее надежный, чем "стандартный Perl-овский",
но зато нормально работающий и под Win9x, и под UNIX-подобными системами. 

 
« Предыдущая статья