PHP锁定/确保给定脚本在任何给定时间只运行一次

时间:2012-01-31 16:55:55

标签: php locking

我正在尝试编写一个PHP脚本,我希望确保在任何给定时间只运行它的单个实例。所有这些都谈到了不同的锁定方式,竞争条件等等等,这些都是给我的。

我对锁定文件是否可行,或信号量,或使用MySQL锁等等感到困惑。

任何人都可以告诉我:

a)实现此目的的正确方法是什么?

b)指向PHP实现(或易于移植到PHP的东西?)

6 个答案:

答案 0 :(得分:2)

一种方法是使用php函数 flock 和一个虚拟文件,它将充当监视程序。

在我们的工作开始时,如果文件提出 LOCK_EX 标志,退出或等待,则可以完成。

Php flock文档:http://php.net/manual/en/function.flock.php

对于此示例,必须首先创建名为 lock.txt 的文件。

示例1 ,如果另一个双进程正在运行,它将正确退出,而不会重试,给出状态消息。

如果文件 lock.txt 无法访问,它将抛出错误状态。

<?php

$fp = fopen("lock.txt", "r+");

if (!flock($fp, LOCK_EX|LOCK_NB, $blocked)) {
    if ($blocked) {

        // another process holds the lock
        echo "Couldn't get the lock! Other script in run!\n"; 

    }
    else {
        // couldn't lock for another reason, e.g. no such file
        echo "Error! Nothing done.";
    }
}
else {
    // lock obtained
    ftruncate($fp, 0);  // truncate file

    // Your job here 
    echo "Job running!\n";

    // Leave a breathe
    sleep(3);

    fflush($fp);            // flush output before releasing the lock
    flock($fp, LOCK_UN);    // release the lock

}

fclose($fp); // Empty memory

示例2 ,我们希望进程等待,以便在队列之后执行,如果有的话:

<?php

$fp = fopen("lock.txt", "r+");

if (flock($fp, LOCK_EX)) {  // acquire an exclusive lock
    ftruncate($fp, 0);      // truncate file

    // Your job here 
    echo "Job running!\n";

    // Leave a breathe
    sleep(3);

    fflush($fp);            // flush output before releasing the lock
    flock($fp, LOCK_UN);    // release the lock
}

fclose($fp);
通过在脚本结束时创建和删除文件, fopen 进入 x 模式也是可行的。

  

创建并仅供写作;将文件指针放在   文件的开头。如果文件已存在,则调用fopen()   将返回FALSE

失败      

http://php.net/manual/en/function.fopen.php

答案 1 :(得分:1)

// borrow from 2 anwsers on stackoverflow
function IsProcessRunning($pid) {
    return shell_exec("ps aux | grep " . $pid . " | wc -l") > 2;
}

function AmIRunning($process_file) {
    // Check I am running from the command line
    if (PHP_SAPI != 'cli') {
        error('Run me from the command line');
        exit;
    }

    // Check if I'm already running and kill myself off if I am
    $pid_running = false;
    $pid = 0;
    if (file_exists($process_file)) {
        $data = file($process_file);
        foreach ($data as $pid) {
            $pid = (int)$pid;
            if ($pid > 0 && IsProcessRunning($pid)) {
                $pid_running = $pid;
                break;
            }
        }
    }
    if ($pid_running && $pid_running != getmypid()) {
        if (file_exists($process_file)) {
            file_put_contents($process_file, $pid);
        }
        info('I am already running as pid ' . $pid . ' so stopping now');
        return true;
    } else {
        // Make sure file has just me in it
        file_put_contents($process_file, getmypid());
        info('Written pid with id '.getmypid());
        return false;
    }
}

/*
 * Make sure there is only one instance running at a time
 */
$lockdir = '/data/lock';
$script_name = basename(__FILE__, '.php');
// The file to store our process file
$process_file = $lockdir . DS . $script_name . '.pid';

$am_i_running = AmIRunning($process_file);
if ($am_i_running) {
    exit;
}

答案 2 :(得分:1)

使用信号量:

$key = 156478953; //this should be unique for each script
$maxAcquire = 1;
$permissions =0666;
$autoRelease = 1; //releases semaphore when request is shut down (you dont have to worry about die(), exit() or return
$non_blocking = false; //if true, fails instantly if semaphore is not free

$semaphore = sem_get($key, $maxAcquire, $permissions, $autoRelease);
if (sem_acquire($semaphore, $non_blocking ))  //blocking (prevent simultaneous multiple executions)
{
    processLongCalculation();
}
sem_release($semaphore);

见:

https://www.php.net/manual/en/function.sem-get.php

https://www.php.net/manual/en/function.sem-acquire.php

https://www.php.net/manual/en/function.sem-release.php

答案 3 :(得分:0)

您可以选择最适合您项目的解决方案,实现这两种操作的简单方法是文件锁定或数据库锁定。

对于文件锁定的实现,请检查http://us2.php.net/flock

如果您已经使用了数据库,请创建一个表,为该脚本生成已知令牌,将其放在那里,然后在脚本结束后将其删除。为避免错误问题,您可以使用到期时间。

答案 4 :(得分:0)

答案 5 :(得分:0)

如果你在linux上使用php,我认为最实用的方法是:

<?php
 if(shell_exec('ps aux | grep '.__FILE__.' | wc  -l')>3){
    exit('already running...');
 }
?>

另一种方法是使用文件标志和退出回调, 退出回调将确保文件标志在任何情况下都会重置为0,执行结束也会导致致命错误。

<?php
function exitProcess(){
  if(file_get_contents('inprocess.txt')!='0'){
    file_put_contents('inprocess.txt','0');  
  }
}

if(file_get_contents('inprocess.txt')=='1'){
  exit();
}

file_put_contents('inprocess.txt','1');
register_shutdown_function('exitProcess');

/**
execute stuff
**/
?>