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 Stata 面板数据模型选择
  • ¥20 idea运行测试代码报错问题
  • ¥15 网络监控:网络故障告警通知
  • ¥15 django项目运行报编码错误
  • ¥15 请问这个是什么意思?
  • ¥15 STM32驱动继电器
  • ¥15 Windows server update services
  • ¥15 关于#c语言#的问题:我现在在做一个墨水屏设计,2.9英寸的小屏怎么换4.2英寸大屏
  • ¥15 模糊pid与pid仿真结果几乎一样
  • ¥15 java的GUI的运用