Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > Однопользовательская работа скрипта


Автор: sneJok 27.10.2010, 12:01
Добрый день. Такая задачка:
при выполнении скрипта, не допустить повторную его работу, пока первый пользователь не закончит работу со скриптом.

На пальцах:
1 пользователь начал работу со скриптом, 2 пользователь в это время обращаясь к скрипту получит какое-либо сообщение о занятости.

Вот сам код

Код

<?
$filename='test.txt';
$file=fopen($filename,"r") or die ("Файл не найден");
if (flock($file,2)) {
 echo 'Work';
  sleep(5);
} else {echo 'Not Work';}
@flock($file, 3);
fclose($file);
?>


В чем может быть ошибка? Всегда выводится Work.

Автор: skyboy 27.10.2010, 12:12
с работой flock не знаком, специфики не знаю.
но соображения имеются.
не значит ли использование flock "заблокировать файл и оставить доступ только текущему процессу"? если означает, то у тебя ж все запросы к РНР файлам идут в рамках одного процесса, верно? попробуй увеличить паузу в скрипте и открыть заблокированный файл параллельно при помощи другого процесса. скажем, блокнота. у меня вот файл блокнотом открывается только на чтение во время работы РНР скрипта.

Добавлено через 54 секунды
кроме того, в первом же комментарии на php.net/flock вижу следующее:
Цитата

1) flock() is not safe if multiple php sessions are simultaneously locking.
2) fopen( , 'a') is not safe if multiple php sessions are simultaneously appending.
3) usleep and sleep are known for having problems.

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

Добавлено через 3 минуты и 5 секунд
может, http://ua2.php.net/manual/en/book.sem.php использовать?

Автор: sneJok 27.10.2010, 12:25
skyboy, благодарю за быстроту и идеи. Видимо по поводу одного процесса вы правы.
По поводу семафоров - надо разбираться, буду гуглить.
Есть еще идейки?

Автор: sneJok 27.10.2010, 12:58
Начал смотреть простейший пример из одной статьи:

Код

<?
$MEMSIZE = 512; //  объём выделяемой разделяемой памяти
$SEMKEY = 1;   //  ключ семафора
$SHMKEY = 2;   //  ключ разделяемой памяти

echo "Старт.\n";

// Создаем семафор
$sem_id = sem_get($SEMKEY, 1);
if ($sem_id === false)
{
    echo "Ошибка при создании семафора";
    exit;
}
else
    echo "Создан семафор $sem_id.\n";

// Занимаем семафор
if (! sem_acquire($sem_id))
{
    echo "Ошибка при попытке занять семафор $sem_id.\n";
    sem_remove($sem_id);
    exit;
}
else
    echo "Успешно занят семафор $sem_id.\n";

// Подключаем разделяемую память
$shm_id = shm_attach($SHMKEY, $MEMSIZE);
if ($shm_id === false)
{
    echo "Ошибка при подключении разделяемой памяти.\n";
    sem_remove($sem_id);
    exit;
}
else
    echo "Успешное подключение разделяемой памяти: $shm_id.\n";

// Пишем переменную 1
if (!shm_put_var($shm_id, 1, "Переменная 1"))
{
    echo "Ошибка при попытке записать переменную 1 в разделяемую память $shm_id.\n";

    // Овобождаем ресурсы.
    sem_remove($sem_id);
    shm_remove($shm_id);
    exit;
}
else
    echo "Переменная 1 записана в разделяемую память.\n";

// Пишем переменную 2
if (!shm_put_var($shm_id, 2, "Переменная 2"))
{
    echo "Ошибка при попытке записать переменную 2 в разделяемую память $shm_id.\n";

    // Освобождаем ресурсы.
    sem_remove($sem_id);
    shm_remove ($shm_id);
    exit;
}
else
    echo "Переменная 2 записана в разделяемую память.\n";

// Читаем переменную 1
$var1 = shm_get_var($shm_id, 1);
if ($var1 === false)
{
    echo "Ошибка при попытке прочитать переменную 1 из разделяемой памяти $shm_id, " .
         "возвращенное значение=$var1.\n";
}
else
    echo "Прочитана переменная 1=$var1.\n";

// Читаем переменную 2
$var2 = shm_get_var ($shm_id, 2);
if ($var1 === false)
{
     echo "Ошибка при попытке прочитать переменную 2 из разделяемой памяти $shm_id, " .
          "возвращенное значение=$var2.\n";
}
else
    echo "Прочитана переменная 2=$var2.\n";

// Освобождаем семафор
if (!sem_release($sem_id))
    echo "Ошибка при попытке освободить семафор $sem_id.\n";
else
    echo "Семафор $sem_id освобожден.\n";

// Удаляем сегмент разделяемой памяти
if (shm_remove ($shm_id))
    echo "Сегмент разделяемой памяти успешно удален.\n";
else
    echo "Ошибка при попытке удалить сегмент разделяемой памяти $shm_id.\n";

// Удаляем семафор.
if (sem_remove($sem_id))
    echo "Семафор успешно удален.\n";
else
    echo "Ошибка при попытке удалить семафор $sem_id.\n";

echo "Конец.\n";

?>


На выделенной строке ошибка типа - Call to undefined function: sem_get()

Автор: skyboy 27.10.2010, 13:55
видимо, от того, что http://ua2.php.net/manual/en/sem.installation.php эти функции не активированы :(

Автор: sneJok 28.10.2010, 00:20
Чтобы еще придумать.

Автор: skyboy 28.10.2010, 08:43
проверять не блокировку файла, а его наличие.
соответственно, скрипт в начале работы файл создает, а в конце работы удаляет.

Автор: MoLeX 28.10.2010, 09:01
в flock попробуйте в качестве второго параметра указывать LOCK_EX | LOCK_NB
Код

$filename='test.txt';
$file=fopen($filename,"r") or die ("Файл не найден");
if (flock($file, LOCK_EX | LOCK_NB)) {
 echo 'Work';
  sleep(5);
flock ($file, LOCK_UN);
} else {echo 'Not Work';}
fclose($file);

Автор: BuShaRt 28.10.2010, 15:06
Я предлагаю вынести вопрос с уровня "работа с файлом" на уровень "работа с процессом" и просто за счет дополнительного функционала проверять не ту ли метки (полагаю в БД) о том, что ведется работа с процессом. Метка снимается по условию:
1. Пользователь обратился к другому процессу
2. Пользователь нажал кнопку окончания работы с этим процессом
3. Пользователь N-ое время бездействовал.

По минимуму пользователей можно идентифицировать по IP, но лучше сделать авторизацию.

Автор: sneJok 28.10.2010, 23:01
skyboy, хорошее решение. но вот постоянно удалять\создавать файл...

MoLeX, прямо в ТОЧКУ. Работает. Жаль, что не могу поднять вам репутацию. Огромное спасибо!


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

Огромное Вам спасибо! 

Автор: ksnk 28.10.2010, 23:23
У skyboy, решение более устойчиво к внезапному падению исполняющего работу скрипта. После падения остается файл, у которого можно проверить время создания. Если оно достаточно велико - процесс грохнулся, и его можно еще раз стартовать. При блокировке такой информации не остается. Хотя можно время файла обновлять touch'ем. Хотя если совместить touch с блокировкой - может получится заодно и время последнего исполнения процесса.
Метка в базе данных немного сложное решение - нужно писать больше букв ;) 

Есть еще всякие  Memcache/ тоже можно использовать для семафоров, но как и с семафорами - не в каждом сервере оно есть...

Автор: sneJok 14.11.2010, 14:27
Возвращаюсь опять к теме.
Немного изменена задача: С помощью чего еще можно сделать работу скрипта только для одного пользователя, при этом второму и последующим просто выдавать сообщение о занятости. Место имеет также обращение до 5 одновременных (!) обращений к скрипту и собственно как дать только 1 обращенному отработать?

Реализация с lock не работает, иногда еще одно одновременное подключение проскакивает.

Сделал через файл:
Код

<?
if(file_exists('111.txt')) {echo 'Blocked';} else {
$h = fopen("111.txt","w"); fclose($h);
$filename='dbtime.txt';
$file=fopen($filename,"r+") or die ("Файл не найден");

// Здесь выполняются операции с файлом dbtime.txt чтение+запись

fclose($file);
sleep(1);
unlink("111.txt");
}
?>


Тоже интересная ситуация. При увеличении sleep скрипт более менее стабильнее работает, но все равно пропускает двух пользователей. Самое интересное, если не использовать sleep, скрипт вообще не работает :/. Совсем непонятная логика.

Автор: ksnk 14.11.2010, 14:49
Цитата(sneJok @  14.11.2010,  14:27 Найти цитируемый пост)
Реализация с lock не работает, иногда еще одно одновременное подключение проскакивает.

А пример из мана точно не работает?

Example 611. flock() example
Код

$fp = fopen("/tmp/lock.txt", "w+");

if (flock($fp, LOCK_EX)) { // do an exclusive lock
   fwrite($fp, "Write something here\n");
   flock($fp, LOCK_UN); // release the lock
} else {
   echo "Couldn't lock the file !";
}

fclose($fp);



Автор: sneJok 14.11.2010, 15:08
ksnk, к сожалению, нет. При одновременном обращении - проходит > 1 пользователей.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)