PHP无限while循环阻止对脚本的其他调用


PHP infinite while loop blocks other calls to the script

目标/动机

我想写一个服务,它应该一直运行。但是,在服务已经运行时,应该不可能再次启动该服务。

用例

用户X打开页面myService.php,然后单击页面上的按钮启动服务。之后关闭浏览器。过了一段时间,另一个用户打开浏览器并尝试启动服务。但是该服务应该只启动一次。

(SEMI(伪码

<?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((退出的第一个调用

tldr:不要让用户进入第二个循环,在他们可以按下按钮进入循环之前,请检查服务是否正在运行

我会在数据库或JSON文件中存储一个值(ini文件也可以(。然后,当用户第一次加载页面时,读取该值并查看它是否已经启动。然后,您可以直接向用户显示流程已经启动的消息,而不是让他们按下按钮启动流程,然后向他们显示消息。它省去了一个步骤,让用户更容易,还应该解决第二个循环一直运行到第一个循环停止的问题。

问题是,如果脚本在运行时崩溃,则会将文件标记为打开。有两种方法可以解决这个问题。第一个是stopService函数可以强制文件退出,在where循环中,如果检测到该值,则退出。不过,这需要用户停止服务。第二种方法,也是我建议的方法,是记录服务的日期/时间戳,每次循环开始时,都会更新文件中的日期/时刻。然后在serviceAlreadyStarted()函数中,您可以检查日期/时间是否在有效期内。如果不是,你知道脚本崩溃了,可以向用户显示错误消息。

更新

我注意到你添加了一条评论,说你几乎已经这样做了,但文件仍然打开。请确保在读取/设置值后立即关闭文件。也有可能是循环执行得太快,以至于打开和关闭文件的速度太快,第二个副本也无法访问它。如果是这种情况,我只允许循环每100个循环打开一次文件,这应该允许程序的其他实例也访问该文件。

@Styphon

我已经尝试了很多可能的方法,但都没有成功。请考虑下面的代码。我将描述一些对我来说毫无意义的具体案例

案例1文件serviceStarted.txt存在。没有正在进行的呼叫。我调用脚本,得到的答案是"服务已经启动"。没有延误。

案例2serviceStarted.txt不存在。我称之为剧本。第一个循环正在运行,文件已创建。使用另一个选项卡或浏览器,我再次调用该脚本。大约需要20秒,直到我收到服务正在运行的消息。

案例3(最有趣的一个(与案例#2类似,但我在20秒之前(即第一次调用后4秒(在相关文件夹中手动创建/复制serviceShouldBeStopped.txt。这导致第一和第二循环同时停止。这不是有点奇怪吗?

// 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);
    }
}

相关文章: