doumiebiao6827 2018-02-04 23:15
浏览 147

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

I have a php script that I want to run. But if it is already running I want any new instances to end immediately.

I have seen other similar questions to this but all the solutions do not work for me. I need it to work when opening two tabs in the same browser and loading both tabs at once. Currently all the solutions I have tried will queue the second page load instead of terminating it.

I have tried the following...

Create a file at the start and unlinking it at the end, and if it exists exit:

<?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");
}
?>

I have tried getting a file lock:

<?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";
?>

and I have also tried making a locking database file

<?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);

?>

When I hit the page in Firefox and then try and open the page in Chrome it works. But if I try and open two tabs in Firefox or two tabs in Chrome it seems to ignore the blocking mechanism and just queues the scripts. How can I make it so that even if I am running it in a separate tab it gets blocked?

All of these examples also have a flaw. If you cancel the page load it never performs the unblocking function and you have to manually force it to unblock. Ideally I would like a solution that does not have this issue.

  • 写回答

1条回答 默认 最新

  • dongmin1166 2018-02-04 23:38
    关注

    Try this,

    Don't blindly create the lock file, upon successful start store the process id in the lockfile. Then upon subsequent instances check to see if that process id is still running and if it's not then allow an instance to start.

    This will then recover from a stale lock file, and upon destruct delete the lockfile as normal.

    <?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}!\\\\.\oot\\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.
    ");
            }
    
            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
    
    评论

报告相同问题?

悬赏问题

  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 划分vlan后不通了
  • ¥15 GDI处理通道视频时总是带有白色锯齿
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)
  • ¥15 自适应 AR 模型 参数估计Matlab程序
  • ¥100 角动量包络面如何用MATLAB绘制
  • ¥15 merge函数占用内存过大
  • ¥15 使用EMD去噪处理RML2016数据集时候的原理
  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大