确保一次只运行一个php脚本实例

时间:2018-02-04 23:15:53

标签: php

我有一个我要运行的php脚本。但如果它已经在运行,我希望任何新实例立即结束。

我已经看到了其他类似的问题,但所有的解决方案对我都不起作用。我需要它在同一浏览器中打开两个选项卡并立即加载两个选项卡时才能工作。目前我尝试过的所有解决方案都会将第二页加载排队而不是终止它。

我试过以下......

在开始时创建一个文件并在结尾处取消链接,如果它存在则退出:

<?php
if(file_exists("block.txt"))
{
    die('running');
}
else
{
    file_put_contents("block.txt", "blocking");

    //simulate some work
    sleep(60);

    echo "END";

    unlink("block.txt");
}
?>

我试过获取文件锁:

<?php
class Lock{
    private $fp;
    function __construct(){
        $this->fp=fopen(__File__,'r');
        if (!flock($this->fp,LOCK_EX|LOCK_NB)) {
            die('already running !'.PHP_EOL);
        }       
    }
    function __destruct(){
        flock($this->fp,LOCK_UN);
        fclose($this->fp);  
    }
}

$lock=new Lock();

sleep(60);

echo "END";
?>

我也试过制作一个锁定数据库文件

<?php

$con = mysqli_connect($servername, $username, $password, $dbname);
// Check connection
if (!$con) {
    die("Connection failed: " . mysqli_connect_error());
}

if(!mysqli_query($con, "LOCK TABLES Block WRITE;"))
{
    die("blocked");
}

$result = mysqli_query($con, "Select Count(*) as isBlocked from Block;");

if(!$result)
{
    mysqli_query($con, "UNLOCK TABLES;");
    die("blocked");
}

while($row = mysqli_fetch_assoc($result))
{           
    $isBlocked = $row['isBlocked'];
}

mysqli_free_result($result);

if($isBlocked > 0)
{   
    mysqli_query($con, "UNLOCK TABLES;");
    die("blocked");
}
else
{
    mysqli_query($con, "INSERT INTO Block (x) VALUES (1);");
    mysqli_query($con, "UNLOCK TABLES;");
}

sleep(60);

echo "END";

mysqli_query($con, "Truncate Block;");
mysqli_close($con);

?>

当我在Firefox中点击该页面然后尝试在Chrome中打开该页面时,它可以正常运行。但是,如果我尝试在Firefox中打开两个选项卡或在Chrome中打开两个选项卡,它似乎忽略了阻止机制并且只是将脚本排队。我怎样才能做到这一点,即使我在一个单独的标签中运行它也会被阻止?

所有这些例子也都有缺陷。如果取消页面加载,它永远不会执行解除阻止功能,您必须手动强制它取消阻止。理想情况下,我想要一个没有这个问题的解决方案。

1 个答案:

答案 0 :(得分:1)

试试这个,

不要盲目地创建锁文件,一旦成功启动,就会在lockfile中存储进程ID。然后在后续实例中检查该进程ID是否仍在运行,如果它不是,则允许实例启动。

然后,这将从陈旧的锁定文件中恢复,并在销毁时正常删除锁定文件。

<?php
class pid
{
    protected $pidfile;
    public $running = false;

    public function __construct($directory = '')
    {
        $this->pidfile = (!empty($directory) ? $directory.'/' : null) . basename($_SERVER['PHP_SELF']) . '.pid';

        if (is_writable($this->pidfile) || is_writable($directory)) {
            if (file_exists($this->pidfile)) {
                $pid = (int) file_get_contents($this->pidfile);
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
                    $wmi = new COM("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2");
                    $procs = $wmi->ExecQuery("SELECT * FROM Win32_Process WHERE ProcessId='".$pid."'");
                    foreach ($procs as $proc) {
                        $proc->Terminate();
                        $this->running = true;
                    }
                } else {
                    if (posix_kill($pid, 0)) {
                        $this->running = true;
                    }
                }
            }
        } else {
            die("Cannot write to pid file '{$this->pidfile}'. Program execution halted.\n");
        }

        if (!$this->running) {
            file_put_contents($this->pidfile, getmypid());
        }
    }

    public function __destruct()
    {
        if (!$this->running && file_exists($this->pidfile) && is_writeable($this->pidfile)) {
            unlink($this->pidfile);
        }
    }
}

$pid = new pid(dirname(__FILE__));

if ($pid->running) {
    exit;
}

// your code