我正在使用cron系统,并且需要一次只执行一次脚本。通过使用以下代码,我第一次执行脚本并在它循环时(为了延迟目的),再次执行它,但file_exists总是返回false,而第一次执行在循环完成后返回文件内容。
Cronjob.php:
include "Locker.class.php";
Locker::$LockName = __DIR__.'/OneTime_[cron].lock';
$Locker = new Locker();
for ($i = 0 ; $i < 1000000; $i++){
echo 'Z';
$z = true;
ob_end_flush();
ob_start();
}
Locker.class.php:
class Locker{
static $LockName;
function __construct($Expire){
if (!basename(static::$LockName)){
die('Locker: Not a filename.');
}
// It doesn't help
clearstatcache();
if (file_exists(static::$LockName)){ // returns false always
die('Already running');
} else {
$myfile = fopen(static::$LockName, "x"); // Tried with 'x' and 'w', no luck
fwrite($myfile, 'Keep it alive'); // Tried with file_put_content also, no luck
fclose($myfile);
}
// The following function returns true by the way!
// echo file_exists(static::$LockName);
}
function __destruct(){
// It outputs content
echo file_get_contents(static::$LockName);
unlink(static::$LockName);
}
}
有什么问题?为什么file_exists总是返回false?
答案 0 :(得分:1)
我怀疑PHP解析器已经注意到你从不使用变量$Locker
,因此它会立即销毁运行析构函数并删除文件的对象。尝试在循环后对对象进行引用:
include "Locker.class.php";
Locker::$LockName = __DIR__.'/OneTime_[cron].lock';
$Locker = new Locker();
for ($i = 0 ; $i < 1000000; $i++){
echo 'Z';
$z = true;
ob_end_flush();
ob_start();
}
var_dump($Locker);
答案 1 :(得分:1)
如果您的目标是阻止可能长时间运行的作业同时执行多个副本,您可以采用更简单的方法,只需flock()
文件本身。
这将进入cronjob.php
<?php
$wb = false;
$fp = fopen(__FILE__, 'r');
if (!$fp) die("Could not open file");
$locked = flock($fp, LOCK_EX | LOCK_NB, $wb);
if (!$locked) die("Couldn't acquire lock!\n");
// do work here
sleep(20);
flock($fp, LOCK_UN);
fclose($fp);
为了解决您的实际问题,我发现通过运行代码,文件消失的原因是因为在后续调用中,如果作业正在运行,则输出Already running
,然后第二个脚本调用析构函数并在初始任务完成运行之前删除该文件。
上面的flock方法解决了这个问题。否则,您需要确保只有实际创建锁定文件的进程才能删除它(并注意它永远不会被遗忘太久)。