Блокировка одновременной записи в файл без использования функции flock
При работе 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-подобными системами.
При разработке своих CGI-скриптов я предпочитаю этот метод. В частности,
в моем "Скрипте для ведения логов и учета посещений"
описанным выше образом блокируется одновременное использование трех используемых скриптом файлов данных,
и в процессе многолетнего использования мной этого CGI-скрипта никаких эксцессов,
связанных конкретно с этим, не было.
Автор: Андрей Черный
//angel07.webservis.ru
программирование CGI-скриптов на Perl;
авторская коллекция скриптов;
статьи о хостинге;
интернет-пользователю.
|