PHP бесконечный цикл while блокирует другие вызовы скрипта

AIM / МОТИВАЦИЯ

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

Случай использования

Пользователь X открывает страницу myService.php и запускает сервис, нажав кнопку на странице. После этого закрывается браузер.
Через некоторое время другой пользователь открывает браузер и пытается запустить службу.
Но сервис должен быть запущен только один раз.

(ПОЛУ) ПСЕВДО КОД

<?php

ignore_user_abort(TRUE);
set_time_limit(0);
$sleepTime = 60;

while(1){

if( serviceAlreadyStarted() ){
echo "Service has already been started.";
exit;
}

if( shouldServiceBeStopped() ){
echo "Service has been stopped.";
exit;
}
//  DO SOMETHING
sleep($sleepTime);
}
?>

ОЖИДАЕМОЕ ПОВЕДЕНИЕ

При втором вызове для повторного включения услуги пользователь получает сообщение немедленно.

АКТУАЛЬНОЕ ПОВЕДЕНИЕ

Второй вызов сценария не дает ответа до тех пор, пока первый вызов не будет завершен вторым условием shouldServiceBeStopped ()

2

Решение

tldr: не позволяйте пользователю войти во второй цикл, проверьте, работает ли служба до они могут нажать кнопку, чтобы войти в цикл.

Я хотел бы сохранить значение в базе данных или JSON-файл (ini-файл тоже хорошо). Затем, когда пользователь впервые загружает страницу, прочитайте значение и посмотрите, запущено ли оно уже. Затем вы можете отобразить сообщение непосредственно пользователю о том, что процесс уже запущен, вместо того, чтобы заставить его нажать кнопку, чтобы запустить его, а затем показать ему сообщение. Это сокращает шаг и упрощает работу для пользователя, а также должно решить вашу проблему, когда второй цикл продолжает работать до тех пор, пока первый цикл не будет остановлен.

Проблема заключается в том, что если ваш скрипт вылетает во время работы, он оставит файл помеченным как открытый. Есть два способа обойти это. Первый твой stopService Функция может принудительно завершить работу файла, и в вашем цикле where, если обнаружит это значение, завершиться. Это требует, чтобы пользователь остановил службу, хотя. Второй способ, который я бы порекомендовал, состоит в том, чтобы записать метку даты / времени службы, и каждый раз при запуске цикла обновлять дату / время в файле. Тогда в вашем serviceAlreadyStarted() функцию, которую вы можете проверить, чтобы увидеть, находится ли дата / время в течение допустимого периода. Если это не так, вы знаете, что скрипт потерпел крах и может отобразить сообщение об ошибке для пользователя.

ОБНОВИТЬ

Я заметил, что вы добавили комментарий, что вы уже сделали это, но файл остается открытым. Убедитесь, что вы закрываете файл сразу после чтения / установки значения. Также возможно, что ваш цикл выполняется так быстро, что он открывает и закрывает файл слишком быстро, чтобы вторая копия тоже имела к нему доступ. Если это так, я бы позволял циклу открывать файл каждые 100 или около того циклов, что должно позволить другим экземплярам программы получить доступ к файлу.

1

Другие решения

@Styphon

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

СЛУЧАЙ 1
Файл serviceStarted.txt существует. Там нет бегущего вызова. Я вызываю сценарий и сразу же получаю ответ: «Сервис уже запущен». Там нет задержки.

ДЕЛО 2
serviceStarted.txt не существует. Я называю сценарий. Первый цикл работает и файл создан. Используя другую вкладку или браузер, я снова вызываю скрипт. Требуется ок. 20 секунд, пока я не получу сообщение о том, что служба работает.

Случай 3 (самый интересный)
Как и в случае № 2, но я создаю / копирую serviceShouldBeStopped.txt вручную в / в соответствующую папку до истечения 20 секунд (скажем, через 4 секунды после первого вызова). Это вызывает остановку как первого, так и второго цикла одновременно. Это не немного странно?

// Very first check:
if (is_file("serviceStarted.txt")) {
echo "<br> Service has already been started.";
} else {
while (1) {
// This check is aquivalent to the shouldServiceBeStopped()
// I copy this txt manually to the folder in order to stopp the first call
if (is_file("serviceShouldStopped.txt")) {
echo "Service stopped.";
exit;
}

// The first call should create this file
if (!is_file("serviceStarted.txt"))
file_put_contents("serviceStarted.txt", "any content");


sleep(60);
}
}

ОБНОВИТЬ

// This if statement used in second call
if (isset($_GET['noloop'])) {
echo "loop should not be executed";
}
else {

while (1) {
// This check is aquivalent to the shouldServiceBeStopped()
// I copy this txt manually to the folder in order to stopp the first call
if (file_exists("serviceShouldStopped.txt")) {
echo "Service stopped at " . date("H:i:s");
exit;
}
sleep(60);
}
}
0

По вопросам рекламы [email protected]